diff --git a/src/Paramore.Brighter/Tasks/BrighterSynchronizationContext.cs b/src/Paramore.Brighter/Tasks/BrighterSynchronizationContext.cs
index 9adc5415b..a6abd7ebb 100644
--- a/src/Paramore.Brighter/Tasks/BrighterSynchronizationContext.cs
+++ b/src/Paramore.Brighter/Tasks/BrighterSynchronizationContext.cs
@@ -33,10 +33,8 @@ namespace Paramore.Brighter.Tasks
/// Only uses one thread, so predictable performance, but may have many messages queued. Once queue length exceeds
/// buffer size, we will stop reading new work.
///
- internal class BrighterSynchronizationContext : SynchronizationContext
+ public class BrighterSynchronizationContext : SynchronizationContext
{
- private readonly ExecutionContext? _executionContext;
-
///
/// Gets the synchronization helper.
///
@@ -62,7 +60,6 @@ internal class BrighterSynchronizationContext : SynchronizationContext
public BrighterSynchronizationContext(BrighterSynchronizationHelper synchronizationHelper)
{
SynchronizationHelper = synchronizationHelper;
- _executionContext = ExecutionContext.Capture();
}
///
@@ -132,8 +129,7 @@ public override void Post(SendOrPostCallback callback, object? state)
Debug.IndentLevel = 0;
if (callback == null) throw new ArgumentNullException(nameof(callback));
- var ctxt = ExecutionContext.Capture();
- bool queued = SynchronizationHelper.Enqueue(new ContextMessage(callback, state, ctxt), true);
+ bool queued = SynchronizationHelper.Enqueue(new ContextMessage(callback, state), true);
if (queued) return;
@@ -141,11 +137,18 @@ public override void Post(SendOrPostCallback callback, object? state)
//mostly this seems to be a problem with the task we are running completing, but work is still being queued to the
//synchronization context.
var contextCallback = new ContextCallback(callback);
- if (ctxt != null && ctxt != _executionContext)
- ExecuteOnCallersContext(contextCallback, state, ctxt);
- else
- ExecuteImmediately(contextCallback, state);
Debug.WriteLine(string.Empty);
+ Debug.IndentLevel = 1;
+ Debug.WriteLine($"BrighterSynchronizationContext: Post Failed to queue {callback.Method.Name} on thread {Thread.CurrentThread.ManagedThreadId}");
+ Debug.WriteLine($"BrighterSynchronizationContext: Parent Task {ParentTaskId}");
+ Debug.IndentLevel = 0;
+
+ //just execute inline
+ // current thread already owns the context, so just execute inline to prevent deadlocks
+ //if (BrighterSynchronizationHelper.Current == SynchronizationHelper)
+ //SynchronizationHelper.ExecuteImmediately(contextCallback, state);
+ //else
+ base.Post(callback, state);
}
@@ -169,8 +172,7 @@ public override void Send(SendOrPostCallback callback, object? state)
}
else
{
- var ctxt = ExecutionContext.Capture();
- var task = SynchronizationHelper.MakeTask(new ContextMessage(callback, state, ctxt));
+ var task = SynchronizationHelper.MakeTask(new ContextMessage(callback, state));
if (!task.Wait(Timeout)) // Timeout mechanism
throw new TimeoutException("BrighterSynchronizationContext: Send operation timed out.");
}
diff --git a/src/Paramore.Brighter/Tasks/ContextMessage.cs b/src/Paramore.Brighter/Tasks/ContextMessage.cs
index 038648bdf..0804f431f 100644
--- a/src/Paramore.Brighter/Tasks/ContextMessage.cs
+++ b/src/Paramore.Brighter/Tasks/ContextMessage.cs
@@ -16,11 +16,9 @@ public struct ContextMessage
///
/// The callback to execute.
/// The state to pass to the callback.
- /// The execution context, mainly intended for debugging purposes
- public ContextMessage(SendOrPostCallback callback, object? state, ExecutionContext? ctxt)
+ public ContextMessage(SendOrPostCallback callback, object? state)
{
Callback = callback;
State = state;
- Context = ctxt;
}
}
diff --git a/tests/Paramore.Brighter.Core.Tests/Tasks/BrighterSynchronizationContextsTests.cs b/tests/Paramore.Brighter.Core.Tests/Tasks/BrighterSynchronizationContextsTests.cs
index 99abe8ee3..92f3108fd 100644
--- a/tests/Paramore.Brighter.Core.Tests/Tasks/BrighterSynchronizationContextsTests.cs
+++ b/tests/Paramore.Brighter.Core.Tests/Tasks/BrighterSynchronizationContextsTests.cs
@@ -151,26 +151,26 @@ public void Current_WithoutAsyncContext_IsNull()
}
[Fact]
- public void Current_FromAsyncContext_IsAsyncContext()
+ public void Current_FromBrighterSynchronizationHelper_IsBrighterSynchronizationHelper()
{
- BrighterSynchronizationHelper observedContext = null;
- var context = new BrighterSynchronizationHelper();
+ BrighterSynchronizationHelper observedHelper = null;
+ var helper = new BrighterSynchronizationHelper();
- var task = context.Factory.StartNew(
- () => { observedContext = BrighterSynchronizationHelper.Current; },
- context.Factory.CancellationToken,
- context.Factory.CreationOptions | TaskCreationOptions.DenyChildAttach,
- context.TaskScheduler);
+ var task = helper.Factory.StartNew(
+ () => { observedHelper = BrighterSynchronizationHelper.Current; },
+ helper.Factory.CancellationToken,
+ helper.Factory.CreationOptions | TaskCreationOptions.DenyChildAttach,
+ helper.TaskScheduler);
- context.Execute(task);
+ helper.Execute(task);
- observedContext.Should().Be(context);
+ observedHelper.Should().Be(helper);
}
[Fact]
- public void SynchronizationContextCurrent_FromAsyncContext_IsAsyncContextSynchronizationContext()
+ public void SynchronizationContextCurrent_FromBrighterSynchronizationHelper_IsBrighterSynchronizationHelperSynchronizationContext()
{
- System.Threading.SynchronizationContext observedContext = null;
+ System.Threading.SynchronizationContext? observedContext = null;
var context = new BrighterSynchronizationHelper();
var task = context.Factory.StartNew(
@@ -362,6 +362,35 @@ public async Task Task_AfterExecute_Runs_On_ThreadPool()
threadPoolExceptionRan.Should().BeTrue();
}
+
+ [Fact]
+ public async Task SynchronizationContextCurrent_FromAsyncContext_PostFromAnotherThread()
+ {
+ System.Threading.SynchronizationContext? observedContext = null;
+ var helper = new BrighterSynchronizationHelper();
+
+ var task = helper.Factory.StartNew(
+ () => { observedContext =BrighterSynchronizationContext.Current; },
+ helper.Factory.CancellationToken,
+ helper.Factory.CreationOptions | TaskCreationOptions.DenyChildAttach,
+ helper.TaskScheduler);
+
+ //this should complete the task
+ helper.Execute(task);
+
+ //but this simulates us being disposed
+ observedContext.OperationCompleted();
+
+ //we may be called on a different thread
+ int value = 1;
+ await Task.Run(() =>
+ {
+ observedContext .Post(_ => value = 2, null);
+ });
+
+ value.Should().Be(2);
+
+ }
[Fact]
public void SynchronizationContext_IsEqualToCopyOfItself()