Skip to content

Commit

Permalink
Created a custom awaiter that guarantees that we complete on a differ…
Browse files Browse the repository at this point in the history
…ent threadpool thread. Previously, an awaited Task had a non-zero chance of completing before it was awaited, in which case execution would continue on the same thread.
  • Loading branch information
leeoades committed Dec 2, 2024
1 parent 7223486 commit e028625
Showing 1 changed file with 24 additions and 4 deletions.
28 changes: 24 additions & 4 deletions test/Stateless.Tests/SynchronizationContextFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
Expand Down Expand Up @@ -41,15 +43,16 @@ private void CaptureSyncContext()

private async Task LoseSyncContext()
{
await Task.Run(() => { }).ConfigureAwait(false); // Switch synchronization context and continue
await new CompletesOnDifferentThreadAwaitable(); // Switch synchronization context and continue
Assert.NotEqual(_customSynchronizationContext, SynchronizationContext.Current);
}

/// <summary>
/// Tests capture the SynchronizationContext at various points through out their execution.
/// This asserts that every capture is the expected SynchronizationContext instance and that is hasn't been lost.
/// Tests capture the SynchronizationContext at various points throughout their execution.
/// This asserts that every capture is the expected SynchronizationContext instance and that it hasn't been lost.
/// </summary>
/// <param name="numberOfExpectedCalls">Ensure that we have the expected number of captures</param>
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
private void AssertSyncContextAlwaysRetained(int numberOfExpectedCalls)
{
Assert.Equal(numberOfExpectedCalls, _capturedSyncContext.Count);
Expand Down Expand Up @@ -154,7 +157,7 @@ public async Task Multiple_Deactivations_should_retain_SyncContext()

// ASSERT
AssertSyncContextAlwaysRetained(3);
}
}

[Fact]
public async Task Multiple_OnEntry_should_retain_SyncContext()
Expand Down Expand Up @@ -338,4 +341,21 @@ public async Task InternalTransition_firing_a_sync_action_should_retain_SyncCont
// ASSERT
AssertSyncContextAlwaysRetained(1);
}

private class CompletesOnDifferentThreadAwaitable
{
public CompletesOnDifferentThreadAwaiter GetAwaiter() => new();

internal class CompletesOnDifferentThreadAwaiter : INotifyCompletion
{
public void GetResult() { }

public bool IsCompleted => false;

public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation());
}
}
}
}

0 comments on commit e028625

Please sign in to comment.