diff --git a/src/DurableEngine/OrchestrationInvoker.cs b/src/DurableEngine/OrchestrationInvoker.cs index ae58b15..e9841e6 100644 --- a/src/DurableEngine/OrchestrationInvoker.cs +++ b/src/DurableEngine/OrchestrationInvoker.cs @@ -76,7 +76,7 @@ internal Hashtable Invoke(IPowerShellServices powerShellServices) while (true) { // block this thread until user-code thread (the PS orchestrator) invokes a DF CmdLet or completes. - var orchestratorReturned = context.SharedMemory.YieldToUserCodeThread(orchestratorReturnedHandle); + var orchestratorReturned = context.SharedMemory.WaitForInvokerThreadTurn(orchestratorReturnedHandle); if (orchestratorReturned) { // The PS orchestrator has a return value, there's no more DF APIs to await. @@ -109,6 +109,12 @@ internal Hashtable Invoke(IPowerShellServices powerShellServices) await task.GetDTFxTask(); } // Exceptions are ignored at this point, they will be re-surfaced by the PS code if left unhandled. catch { } + + // Wake up user-code thread. For a small moment, both the user code thread and the invoker thread + // will be running at the same time. + // However, the invoker thread will block itself again at the start of the next loop until the user-code + // thread yields control. + context.SharedMemory.WakeUserCodeThread(); } }; diff --git a/src/DurableEngine/SharedMemory.cs b/src/DurableEngine/SharedMemory.cs index 20d02a1..c5012a6 100644 --- a/src/DurableEngine/SharedMemory.cs +++ b/src/DurableEngine/SharedMemory.cs @@ -40,25 +40,16 @@ public void YieldToInvokerThread() { // Wake invoker thread. invokerThreadTurn.Set(); - - // Block user-code thread. - userCodeThreadTurn.Reset(); userCodeThreadTurn.WaitOne(); } /// - /// Blocks Orchestration-invoker thread, wakes up user-code thread. - /// This is usually used after the invoker has a result for the PS orchestrator. + /// Blocks Orchestration-invoker thread until the user-code thread completes or yields. /// /// The WaitHandle tracking if the user-code thread completed. /// True if the user-code thread completed, False if it requests an API to be awaited. - public bool YieldToUserCodeThread(WaitHandle completionHandle) + public bool WaitForInvokerThreadTurn(WaitHandle completionHandle) { - // Wake user-code thread - userCodeThreadTurn.Set(); - - // Get invoker thread ready to block - invokerThreadTurn.Reset(); // Wake up when either the user-code returns, or when we're yielded-to for `await`'ing. var index = WaitHandle.WaitAny(new[] { completionHandle, invokerThreadTurn }); @@ -67,13 +58,12 @@ public bool YieldToUserCodeThread(WaitHandle completionHandle) } /// - /// Blocks user code thread if the orchestrator-invoker thread is currently running. - /// This guarantees that the user-code thread and the orchestration-invoker thread run one - /// at a time after this point. + /// Wakes up the user-code thread without blocking the invoker thread. + /// The invoker thread should block itself afterwards to prevent races. /// - public void GuaranteeUserCodeTurn() + public void WakeUserCodeThread() { - userCodeThreadTurn.WaitOne(); + userCodeThreadTurn.Set(); } } } \ No newline at end of file diff --git a/src/DurableEngine/Tasks/DurableTask.cs b/src/DurableEngine/Tasks/DurableTask.cs index 0e1604a..1dd4b72 100644 --- a/src/DurableEngine/Tasks/DurableTask.cs +++ b/src/DurableEngine/Tasks/DurableTask.cs @@ -49,10 +49,6 @@ internal OrchestrationAction GetOrCreateAction() /// Function to write an exception to the pipeline. public void Execute(Action write, Action writeErr) { - // Ensure that a DurableTask in the usercode thread - // only executes while the orchestration-invoker thread is blocked. - OrchestrationContext.SharedMemory.GuaranteeUserCodeTurn(); - DurableTask task = this; if (NoWait)