Skip to content

Commit f95674e

Browse files
authored
Merge pull request #6 from dbosoft/pull/agent_cancelation
added CancellationToken support to Agent
2 parents 5d52962 + 81b6695 commit f95674e

1 file changed

Lines changed: 34 additions & 26 deletions

File tree

src/Dbosoft.Functional/Agent.cs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
11
using System;
2+
using System.Threading;
23
using System.Threading.Tasks;
34
using System.Threading.Tasks.Dataflow;
45

56
namespace Dbosoft.Functional
67
{
78
public static class Agent
89
{
9-
public static IAgent<TMsg> Start<TMsg>(Action<TMsg> action)
10-
=> new StatelessAgent<TMsg>(action);
10+
public static IAgent<TMsg> Start<TMsg>(Action<TMsg> action, CancellationToken cancellationToken = default)
11+
=> new StatelessAgent<TMsg>(action, cancellationToken);
1112

1213
public static IAgent<TMsg> Start<TState, TMsg>
1314
(Func<TState> initState
14-
, Func<TState, TMsg, TState> process)
15-
=> new StatefulAgent<TState, TMsg>(initState(), process);
15+
, Func<TState, TMsg, TState> process, CancellationToken cancellationToken = default)
16+
=> new StatefulAgent<TState, TMsg>(initState(), process, cancellationToken);
1617

1718
public static IAgent<TMsg> Start<TState, TMsg>
1819
(TState initialState
19-
, Func<TState, TMsg, TState> process)
20-
=> new StatefulAgent<TState, TMsg>(initialState, process);
20+
, Func<TState, TMsg, TState> process, CancellationToken cancellationToken = default)
21+
=> new StatefulAgent<TState, TMsg>(initialState, process, cancellationToken);
2122

2223
public static IAgent<TMsg> Start<TState, TMsg>
2324
(TState initialState
24-
, Func<TState, TMsg, Task<TState>> process)
25-
=> new StatefulAgent<TState, TMsg>(initialState, process);
25+
, Func<TState, TMsg, Task<TState>> process, CancellationToken cancellationToken = default)
26+
=> new StatefulAgent<TState, TMsg>(initialState, process, cancellationToken);
2627

2728
public static IAgent<TMsg, TReply> Start<TState, TMsg, TReply>
2829
(TState initialState
29-
, Func<TState, TMsg, (TState, TReply)> process)
30-
=> new TwoWayAgent<TState, TMsg, TReply>(initialState, process);
30+
, Func<TState, TMsg, (TState, TReply)> process, CancellationToken cancellationToken = default)
31+
=> new TwoWayAgent<TState, TMsg, TReply>(initialState, process,cancellationToken);
3132

3233
public static IAgent<TMsg, TReply> Start<TState, TMsg, TReply>
3334
(TState initialState
34-
, Func<TState, TMsg, Task<(TState, TReply)>> process)
35-
=> new TwoWayAgent<TState, TMsg, TReply>(initialState, process);
35+
, Func<TState, TMsg, Task<(TState, TReply)>> process, CancellationToken cancellationToken = default)
36+
=> new TwoWayAgent<TState, TMsg, TReply>(initialState, process,cancellationToken);
3637
}
3738

3839
public interface IAgent<in TMsg>
@@ -49,14 +50,15 @@ class StatelessAgent<TMsg> : IAgent<TMsg>
4950
{
5051
private readonly ActionBlock<TMsg> _actionBlock;
5152

52-
public StatelessAgent(Action<TMsg> process)
53+
public StatelessAgent(Action<TMsg> process,CancellationToken cancellationToken)
5354
{
54-
_actionBlock = new ActionBlock<TMsg>(process);
55+
_actionBlock = new ActionBlock<TMsg>(process, new ExecutionDataflowBlockOptions{CancellationToken = cancellationToken});
5556
}
5657

57-
public StatelessAgent(Func<TMsg, Task> process)
58+
public StatelessAgent(Func<TMsg, Task> process, CancellationToken cancellationToken)
5859
{
59-
_actionBlock = new ActionBlock<TMsg>(process);
60+
_actionBlock = new ActionBlock<TMsg>(process, new ExecutionDataflowBlockOptions { CancellationToken = cancellationToken });
61+
6062
}
6163

6264
public void Tell(TMsg message) => _actionBlock.Post(message);
@@ -68,32 +70,36 @@ class StatefulAgent<TState, TMsg> : IAgent<TMsg>
6870
private readonly ActionBlock<TMsg> _actionBlock;
6971

7072
public StatefulAgent(TState initialState
71-
, Func<TState, TMsg, TState> process)
73+
, Func<TState, TMsg, TState> process, CancellationToken cancellationToken)
7274
{
7375
_state = initialState;
7476

7577
_actionBlock = new ActionBlock<TMsg>(
76-
msg => _state = process(_state, msg)); // process the message with the current state, and store the resulting new state as the current state of the agent
78+
msg => _state = process(_state, msg),// process the message with the current state, and store the resulting new state as the current state of the agent
79+
new ExecutionDataflowBlockOptions{CancellationToken = cancellationToken});
7780
}
7881

7982
public StatefulAgent(TState initialState
80-
, Func<TState, TMsg, Task<TState>> process)
83+
, Func<TState, TMsg, Task<TState>> process, CancellationToken cancellationToken)
8184
{
8285
_state = initialState;
8386

8487
_actionBlock = new ActionBlock<TMsg>(
85-
async msg => _state = await process(_state, msg));
88+
async msg => _state = await process(_state, msg),
89+
new ExecutionDataflowBlockOptions{CancellationToken = cancellationToken });
8690
}
8791

8892
public void Tell(TMsg message) => _actionBlock.Post(message);
8993
}
9094

9195
class TwoWayAgent<TState, TMsg, TReply> : IAgent<TMsg, TReply>
9296
{
97+
private readonly CancellationToken _cancellationToken;
9398
private readonly ActionBlock<(TMsg, TaskCompletionSource<TReply>)> _actionBlock;
9499

95-
public TwoWayAgent(TState initialState, Func<TState, TMsg, (TState, TReply)> process)
100+
public TwoWayAgent(TState initialState, Func<TState, TMsg, (TState, TReply)> process, CancellationToken cancellationToken)
96101
{
102+
_cancellationToken = cancellationToken;
97103
var state = initialState;
98104

99105
_actionBlock = new ActionBlock<(TMsg, TaskCompletionSource<TReply>)>(
@@ -102,17 +108,19 @@ public TwoWayAgent(TState initialState, Func<TState, TMsg, (TState, TReply)> pro
102108
var result = process(state, t.Item1);
103109
state = result.Item1;
104110
t.Item2.SetResult(result.Item2);
105-
});
111+
}, new ExecutionDataflowBlockOptions{CancellationToken = cancellationToken});
106112
}
107113

108114
// creates a 2-way agent with an async processing func
109-
public TwoWayAgent(TState initialState, Func<TState, TMsg, Task<(TState, TReply)>> process)
115+
public TwoWayAgent(TState initialState, Func<TState, TMsg, Task<(TState, TReply)>> process, CancellationToken cancellationToken)
110116
{
111117
var state = initialState;
118+
_cancellationToken = cancellationToken;
112119

113120
_actionBlock = new ActionBlock<(TMsg, TaskCompletionSource<TReply>)>(
114121
async t =>
115122
{
123+
116124
await process(state, t.Item1)
117125
.ContinueWith(task =>
118126
{
@@ -125,16 +133,16 @@ await process(state, t.Item1)
125133
}
126134
})
127135
.ConfigureAwait(false);
128-
});
136+
}, new ExecutionDataflowBlockOptions { CancellationToken = cancellationToken });
129137
}
130138

131139
public Task<TReply> Tell(TMsg message)
132140
{
133141
var tcs = new TaskCompletionSource<TReply>();
134142
_actionBlock.Post((message, tcs));
135-
143+
136144
// this will help to relax the task scheduler, for some reason the task may block if directly returned
137-
tcs.Task.Wait(10000);
145+
tcs.Task.Wait(_cancellationToken);
138146
return tcs.Task;
139147
}
140148
}

0 commit comments

Comments
 (0)