Skip to content

Commit 53288c8

Browse files
committed
Set CallbackOnReceive back to one shot.
1 parent d30cfcb commit 53288c8

File tree

9 files changed

+295
-195
lines changed

9 files changed

+295
-195
lines changed

Readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,5 @@ There are four channel types.
129129

130130
* _[Channel](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorks/Channels/Channel.cs)_ - Forward published messages to all subscribers. One-way. Used for 1:1 unicasting, 1:N broadcasting and N:1 message aggregation. [Example](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorksTests/Examples/BasicExamples.cs#L20).
131131
* _[QueueChannel](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorks/Channels/QueueChannel.cs)_ - Forward a published message to only one of the subscribers. One-way. Used for 1:N/N:N load balancing. [Example](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorksTests/QueueChannelTests.cs#L22).
132-
* _[RequestReplyChannel](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorks/Channels/RequestReplyChannel.cs)_ - Subscribers respond to requests from publishers. Two-way. Used for 1:1/N:1 request/reply messaging, 1:N/N:M bulk queries to multiple nodes. [Example](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorksTests/RequestReplyChannelTests.cs#L20).
133-
* _[SnapshotChannel](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorks/Channels/SnapshotChannel.cs)_ - Subscribers are also notified when they start subscribing, and separately thereafter. One-way. Used for replication with incremental update notifications. Only one responder can be handled within a single channel. [Example](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorksTests/Examples/BasicExamples.cs#L181).
132+
* _[RequestReplyChannel](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorks/Channels/RequestReplyChannel.cs)_ - Subscribers respond to requests from publishers. Two-way. Used for 1:1/N:1 request/reply messaging, 1:N/N:M bulk queries to multiple nodes. [Example](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorksTests/RequestReplyChannelTests.cs).
133+
* _[SnapshotChannel](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorks/Channels/SnapshotChannel.cs)_ - Subscribers are also notified when they start subscribing, and separately thereafter. One-way. Used for replication with incremental update notifications. Only one responder can be handled within a single channel. [Example](https://github.com/tosh-coding/AsyncFiberWorks/blob/main/src/AsyncFiberWorksTests/Examples/BasicExamples.cs#L189).
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using AsyncFiberWorks.Core;
2+
using System;
3+
4+
namespace AsyncFiberWorks.Channels
5+
{
6+
/// <summary>
7+
/// Extensions of actions and contexts.
8+
/// </summary>
9+
public static class ActionAndFiberExtensions
10+
{
11+
/// <summary>
12+
/// Create an action to be executed on the specified context.
13+
/// </summary>
14+
/// <typeparam name="T"></typeparam>
15+
/// <param name="fiber">Target fiber.</param>
16+
/// <param name="action">Action.</param>
17+
/// <returns>Action with enqueue.</returns>
18+
public static Action<T> CreateAction<T>(this IExecutionContext fiber, Action<T> action)
19+
{
20+
if (fiber == null)
21+
{
22+
return action;
23+
}
24+
else
25+
{
26+
return (msg) => fiber.Enqueue(() => action(msg));
27+
}
28+
}
29+
}
30+
}

src/AsyncFiberWorks/Channels/IReply.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ public interface IReply<M> : IDisposable
1111
/// <summary>
1212
/// Receive a single response. Can be called repeatedly for multiple replies.
1313
/// </summary>
14-
/// <param name="result"></param>
15-
/// <returns></returns>
14+
/// <param name="result">A message.</param>
15+
/// <returns>Returns false if the buffer is empty or the object has been disposed.</returns>
1616
bool TryReceive(out M result);
1717

1818
/// <summary>
19-
/// Set up on-receive callbacks.
20-
/// Also called if timed out. In that case, TryReceive will fail.
19+
/// Set up on-receive callbacks. It is a one-time call.
2120
/// </summary>
22-
/// <param name="callbackOnReceive"></param>
23-
/// <returns></returns>
21+
/// <param name="callbackOnReceive">Message receive handler.</param>
22+
/// <returns>Returns false if it has already been disposed.</returns>
2423
bool SetCallbackOnReceive(Action callbackOnReceive);
2524
}
2625
}

src/AsyncFiberWorks/Channels/IRequest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public interface IRequest<R, M>
1515
/// <summary>
1616
/// Send one or more responses.
1717
/// </summary>
18-
/// <param name="replyMsg"></param>
19-
/// <returns></returns>
18+
/// <param name="replyMsg">A message</param>
19+
/// <returns>Returns false if it has already been disposed.</returns>
2020
bool SendReply(M replyMsg);
2121
}
2222
}

src/AsyncFiberWorks/Channels/RequestReplyChannelRequest.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public TRequestMessage Request
2323

2424
public bool SendReply(TReplyMessage response)
2525
{
26+
Action action;
2627
lock (_lock)
2728
{
2829
if (_disposed)
@@ -31,10 +32,11 @@ public bool SendReply(TReplyMessage response)
3132
}
3233
_resp.Enqueue(response);
3334

34-
var callbackOnReceive = _callbackOnReceive;
35-
callbackOnReceive?.Invoke();
36-
return true;
35+
action = _callbackOnReceive;
36+
_callbackOnReceive = null;
3737
}
38+
action?.Invoke();
39+
return true;
3840
}
3941

4042
public bool TryReceive(out TReplyMessage result)
@@ -53,24 +55,28 @@ public bool TryReceive(out TReplyMessage result)
5355

5456
public bool SetCallbackOnReceive(Action callbackOnReceive)
5557
{
56-
if (callbackOnReceive == null)
57-
{
58-
throw new ArgumentNullException(nameof(callbackOnReceive));
59-
}
58+
bool hasResponse = false;
6059
lock (_lock)
6160
{
6261
if (_disposed)
6362
{
6463
return false;
6564
}
6665

67-
_callbackOnReceive = callbackOnReceive;
68-
69-
if (_resp.Count > 0)
66+
hasResponse = _resp.Count > 0;
67+
if (hasResponse)
68+
{
69+
_callbackOnReceive = null;
70+
}
71+
else
7072
{
71-
callbackOnReceive();
73+
_callbackOnReceive = callbackOnReceive;
7274
}
7375
}
76+
if (hasResponse)
77+
{
78+
callbackOnReceive?.Invoke();
79+
}
7480
return true;
7581
}
7682

src/AsyncFiberWorks/Core/ActionWithContext.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@
33
namespace AsyncFiberWorks.Core
44
{
55
/// <summary>
6-
/// A pair of Action and Fiber.
6+
/// An action with an execution context.
77
/// </summary>
88
/// <typeparam name="T"></typeparam>
99
public class ActionWithContext<T>
1010
{
11+
/// <summary>
12+
/// Action.
13+
/// </summary>
1114
private readonly Action<T> _action;
15+
16+
/// <summary>
17+
/// The context in which the action should be executed.
18+
/// </summary>
1219
private readonly IExecutionContext _fiber;
1320

1421
/// <summary>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace AsyncFiberWorks.Core
5+
{
6+
/// <summary>
7+
/// One shot executor. It is executed only once the first time.
8+
/// </summary>
9+
public class OneShotExecutor : IExecutor
10+
{
11+
private bool _fired = false;
12+
13+
/// <summary>
14+
/// Execute only first one.
15+
/// </summary>
16+
/// <param name="toExecute"></param>
17+
public void Execute(List<Action> toExecute)
18+
{
19+
if (_fired) return;
20+
foreach (var action in toExecute)
21+
{
22+
_fired = true;
23+
Execute(action);
24+
break;
25+
}
26+
}
27+
28+
///<summary>
29+
/// Executes a single action.
30+
///</summary>
31+
///<param name="toExecute"></param>
32+
public void Execute(Action toExecute)
33+
{
34+
if (!_fired)
35+
{
36+
_fired = true;
37+
toExecute();
38+
}
39+
}
40+
}
41+
}

src/AsyncFiberWorksTests/Examples/BasicExamples.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ public void RequestReply()
158158
{
159159
var channel = new RequestReplyChannel<string, string>();
160160
var subscriptionFiber = fiber.BeginSubscription();
161-
var actionWithContext = new ActionWithContext<IRequest<string, string>>(req => req.SendReply("bye"), fiber);
162-
var subscriptionChannel = channel.AddResponder(actionWithContext.OnReceive);
161+
var subscriptionChannel = channel.AddResponder(
162+
fiber.CreateAction<IRequest<string, string>>(req => req.SendReply("bye")));
163163
subscriptionFiber.AppendDisposable(subscriptionChannel);
164164

165165
var reply = channel.SendRequest("hello");
@@ -201,8 +201,8 @@ public void Snapshot()
201201
}
202202
};
203203
var subscriptionFiber = fiberReply.BeginSubscription();
204-
var actionWithContext = new ActionWithContext<IRequest<object, int>>(request => request.SendReply(reply()), fiberReply);
205-
var subscriptionChannel = channel.ReplyToPrimingRequest(actionWithContext.OnReceive);
204+
var subscriptionChannel = channel.ReplyToPrimingRequest(
205+
fiberReply.CreateAction<IRequest<object, int>>(request => request.SendReply(reply())));
206206
subscriptionFiber.AppendDisposable(subscriptionChannel);
207207
Assert.AreEqual(1, channel.NumSubscribers);
208208

0 commit comments

Comments
 (0)