Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Sentry.Unity/ScreenshotEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public SentryEvent Process(SentryEvent @event)
// Only ever capture one screenshot per frame
if (Interlocked.CompareExchange(ref _isCapturingScreenshot, 1, 0) == 0)
{
_sentryMonoBehaviour.StartCoroutine(CaptureScreenshotCoroutine(@event.EventId));
_options.LogDebug("Starting coroutine to capture a screenshot.");
_sentryMonoBehaviour.QueueCoroutine(CaptureScreenshotCoroutine(@event.EventId));
}

return @event;
Expand Down
30 changes: 30 additions & 0 deletions src/Sentry.Unity/SentryMonoBehaviour.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Threading;
using Sentry.Unity.Integrations;
using UnityEngine;

Expand All @@ -9,6 +10,7 @@ internal interface ISentryMonoBehaviour
{
event Action? ApplicationResuming;
public Coroutine StartCoroutine(IEnumerator routine);
public void QueueCoroutine(IEnumerator routine);
}

/// <summary>
Expand Down Expand Up @@ -51,6 +53,34 @@ public void StartAwakeSpan(MonoBehaviour monoBehaviour) =>
/// </summary>
public partial class SentryMonoBehaviour
{
private volatile IEnumerator? _queuedCoroutine;

public void QueueCoroutine(IEnumerator routine)
{
if (MainThreadData.IsMainThread())
{
StartCoroutine(routine);
}
else
{
// Fallback for issues coming from a background thread (e.g., Burst job)
// Screenshot will be captured in the next frame
_queuedCoroutine = routine;
}
}

private void Update()
{
var coroutine = _queuedCoroutine;
if (coroutine == null)
{
return;
}

_queuedCoroutine = null;
StartCoroutine(coroutine);
}

/// <summary>
/// Hook to receive an event when the application gains focus.
/// </summary>
Expand Down
47 changes: 47 additions & 0 deletions test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Sentry.Unity.Tests.Stubs;
using UnityEngine;
Expand Down Expand Up @@ -81,4 +83,49 @@ public void UpdatePauseStatus_ResumedTwice_ApplicationResumingInvokedOnlyOnce()
Assert.AreEqual(1, counter);
}

[Test]
public void QueueCoroutine_CalledOnMainThread_StartsCoroutineImmediately()
{
var sut = _fixture.GetSut();
var coroutineExecuted = false;

IEnumerator TestCoroutine()
{
coroutineExecuted = true;
yield return null;
}

sut.QueueCoroutine(TestCoroutine());

Assert.IsTrue(coroutineExecuted);
}

[UnityTest]
public IEnumerator QueueCoroutine_QueuedOnBackgroundThread_StartsInUpdate()
{
var sut = _fixture.GetSut();
var coroutineExecuted = false;

IEnumerator TestCoroutine()
{
coroutineExecuted = true;
yield return null;
}

var thread = new Thread(() =>
{
sut.QueueCoroutine(TestCoroutine());
});

thread.Start();
thread.Join();

// Coroutine should not have started yet
Assert.IsFalse(coroutineExecuted);

// Wait for the coroutine to execute - trigger `Update`
yield return null;

Assert.IsTrue(coroutineExecuted);
}
}
6 changes: 6 additions & 0 deletions test/Sentry.Unity.Tests/Stubs/SentryTestMonoBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ internal class TestSentryMonoBehaviour : MonoBehaviour, ISentryMonoBehaviour
StartCoroutineCalled = true;
return base.StartCoroutine(routine);
}

public void QueueCoroutine(IEnumerator routine)
{
// For tests, assume we're on the main thread and start immediately
StartCoroutine(routine);
}
}
Loading