In TwoWayAgent.Tell(), the TaskCompletionSource is created with TaskContinuationOptions.RunContinuationsAsynchronously instead of TaskCreationOptions.RunContinuationsAsynchronously. This causes SetResult() to run continuations synchronously, leading to deadlocks when continuations attempt to send additional messages to the same Agent.
// Current (buggy) code in Agent.cs:
var tcs = new TaskCompletionSource<TReply>(TaskContinuationOptions.RunContinuationsAsynchronously);
TaskCompletionSource has no constructor accepting TaskContinuationOptions. The enum gets boxed as object and matched to the (object? state) constructor overload, storing it as AsyncState rather than configuring continuation behavior
var tcs = new TaskCompletionSource<TReply>(TaskCreationOptions.RunContinuationsAsynchronously);
When SetResult() is called:
- Continuations run synchronously on the handler thread
- If a continuation sends another message to the same Agent via Tell(), that message is queued
- The continuation blocks waiting for the queued message's result
- The queued message can't be processed because the ActionBlock handler is still executing SetResult()
- Deadlock
Reproduction
The bug manifests when:
- Using libraries like LanguageExt that chain async operations via Bind/Map
- A continuation from one Agent message attempts to call Tell() on the same Agent
- Intermittent based on continuation chain structure at runtime
Environment
.NET 9.0
Dbosoft.Functional (latest)
Observed when used with YaNco SAP RFC library
In TwoWayAgent.Tell(), the TaskCompletionSource is created with TaskContinuationOptions.RunContinuationsAsynchronously instead of TaskCreationOptions.RunContinuationsAsynchronously. This causes SetResult() to run continuations synchronously, leading to deadlocks when continuations attempt to send additional messages to the same Agent.
// Current (buggy) code in Agent.cs:
var tcs = new TaskCompletionSource<TReply>(TaskContinuationOptions.RunContinuationsAsynchronously);TaskCompletionSource has no constructor accepting TaskContinuationOptions. The enum gets boxed as object and matched to the (object? state) constructor overload, storing it as AsyncState rather than configuring continuation behavior
var tcs = new TaskCompletionSource<TReply>(TaskCreationOptions.RunContinuationsAsynchronously);When SetResult() is called:
Reproduction
The bug manifests when:
Environment
.NET 9.0
Dbosoft.Functional (latest)
Observed when used with YaNco SAP RFC library