Commit c3c5234e authored by Marc Gravell's avatar Marc Gravell

expose the subscription channel on both the queue and the ChannelMessage;...

expose the subscription channel on both the queue and the ChannelMessage; change the callbacks on ChannelMessageQueue to take ChannelMessage, to expose that
parent 86ee27f4
...@@ -146,7 +146,7 @@ async Task RunLoop() ...@@ -146,7 +146,7 @@ async Task RunLoop()
while (!subChannel.IsCompleted) while (!subChannel.IsCompleted)
{ {
var work = await subChannel.ReadAsync(); var work = await subChannel.ReadAsync();
int i = int.Parse(Encoding.UTF8.GetString(work.Value)); int i = int.Parse(Encoding.UTF8.GetString(work.Message));
lock (data) lock (data)
{ {
data.Add(i); data.Add(i);
...@@ -202,9 +202,9 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Sync() ...@@ -202,9 +202,9 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Sync()
var data = new List<int>(count); var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel); var subChannel = await sub.SubscribeAsync(channel);
subChannel.OnMessage((key, val) => subChannel.OnMessage(msg =>
{ {
int i = int.Parse(Encoding.UTF8.GetString(val)); int i = int.Parse(Encoding.UTF8.GetString(msg.Message));
bool pulse = false; bool pulse = false;
lock (data) lock (data)
{ {
...@@ -263,9 +263,9 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Async() ...@@ -263,9 +263,9 @@ public async Task PubSubGetAllCorrectOrder_OnMessage_Async()
var data = new List<int>(count); var data = new List<int>(count);
var subChannel = await sub.SubscribeAsync(channel); var subChannel = await sub.SubscribeAsync(channel);
subChannel.OnMessage((key, val) => subChannel.OnMessage(msg =>
{ {
int i = int.Parse(Encoding.UTF8.GetString(val)); int i = int.Parse(Encoding.UTF8.GetString(msg.Message));
bool pulse = false; bool pulse = false;
lock (data) lock (data)
{ {
......
...@@ -11,26 +11,33 @@ namespace StackExchange.Redis ...@@ -11,26 +11,33 @@ namespace StackExchange.Redis
/// </summary> /// </summary>
public readonly struct ChannelMessage public readonly struct ChannelMessage
{ {
private readonly ChannelMessageQueue _queue; // this is *smaller* than storing a RedisChannel for the subsribed channel
/// <summary> /// <summary>
/// See Object.ToString /// See Object.ToString
/// </summary> /// </summary>
public override string ToString() => ((string)Channel) + ":" + ((string)Value); public override string ToString() => ((string)Channel) + ":" + ((string)Message);
/// <summary> /// <summary>
/// See Object.GetHashCode /// See Object.GetHashCode
/// </summary> /// </summary>
public override int GetHashCode() => Channel.GetHashCode() ^ Value.GetHashCode(); public override int GetHashCode() => Channel.GetHashCode() ^ Message.GetHashCode();
/// <summary> /// <summary>
/// See Object.Equals /// See Object.Equals
/// </summary> /// </summary>
public override bool Equals(object obj) => obj is ChannelMessage cm public override bool Equals(object obj) => obj is ChannelMessage cm
&& cm.Channel == Channel && cm.Value == Value; && cm.Channel == Channel && cm.Message == Message;
internal ChannelMessage(RedisChannel channel, RedisValue value) internal ChannelMessage(ChannelMessageQueue queue, RedisChannel channel, RedisValue value)
{ {
_queue = queue;
Channel = channel; Channel = channel;
Value = value; Message = value;
} }
/// <summary>
/// The channel that the subscription was created from
/// </summary>
public RedisChannel SubscriptionChannel => _queue.Channel;
/// <summary> /// <summary>
/// The channel that the message was broadcast to /// The channel that the message was broadcast to
/// </summary> /// </summary>
...@@ -38,7 +45,7 @@ internal ChannelMessage(RedisChannel channel, RedisValue value) ...@@ -38,7 +45,7 @@ internal ChannelMessage(RedisChannel channel, RedisValue value)
/// <summary> /// <summary>
/// The value that was broadcast /// The value that was broadcast
/// </summary> /// </summary>
public RedisValue Value { get; } public RedisValue Message { get; }
} }
...@@ -48,14 +55,17 @@ internal ChannelMessage(RedisChannel channel, RedisValue value) ...@@ -48,14 +55,17 @@ internal ChannelMessage(RedisChannel channel, RedisValue value)
/// <remarks>To create a ChannelMessageQueue, use ISubscriber.Subscribe[Async](RedisKey)</remarks> /// <remarks>To create a ChannelMessageQueue, use ISubscriber.Subscribe[Async](RedisKey)</remarks>
public sealed class ChannelMessageQueue public sealed class ChannelMessageQueue
{ {
private readonly Channel<ChannelMessage> _channel; private readonly Channel<ChannelMessage> _queue;
private readonly RedisChannel _redisChannel; /// <summary>
/// The Channel that was subscribed for this queue
/// </summary>
public RedisChannel Channel { get; }
private RedisSubscriber _parent; private RedisSubscriber _parent;
/// <summary> /// <summary>
/// See Object.ToString /// See Object.ToString
/// </summary> /// </summary>
public override string ToString() => (string)_redisChannel; public override string ToString() => (string)Channel;
/// <summary> /// <summary>
/// Indicates if all messages that will be received have been drained from this channel /// Indicates if all messages that will be received have been drained from this channel
...@@ -64,10 +74,10 @@ public sealed class ChannelMessageQueue ...@@ -64,10 +74,10 @@ public sealed class ChannelMessageQueue
internal ChannelMessageQueue(RedisChannel redisChannel, RedisSubscriber parent) internal ChannelMessageQueue(RedisChannel redisChannel, RedisSubscriber parent)
{ {
_redisChannel = redisChannel; Channel = redisChannel;
_parent = parent; _parent = parent;
_channel = Channel.CreateUnbounded<ChannelMessage>(s_ChannelOptions); _queue = System.Threading.Channels.Channel.CreateUnbounded<ChannelMessage>(s_ChannelOptions);
_channel.Reader.Completion.ContinueWith( _queue.Reader.Completion.ContinueWith(
(t, state) => ((ChannelMessageQueue)state).IsCompleted = true, this, TaskContinuationOptions.ExecuteSynchronously); (t, state) => ((ChannelMessageQueue)state).IsCompleted = true, this, TaskContinuationOptions.ExecuteSynchronously);
} }
static readonly UnboundedChannelOptions s_ChannelOptions = new UnboundedChannelOptions static readonly UnboundedChannelOptions s_ChannelOptions = new UnboundedChannelOptions
...@@ -76,19 +86,19 @@ internal ChannelMessageQueue(RedisChannel redisChannel, RedisSubscriber parent) ...@@ -76,19 +86,19 @@ internal ChannelMessageQueue(RedisChannel redisChannel, RedisSubscriber parent)
SingleReader = false, SingleReader = false,
AllowSynchronousContinuations = false, AllowSynchronousContinuations = false,
}; };
internal void Subscribe(CommandFlags flags) => _parent.Subscribe(_redisChannel, HandleMessage, flags); internal void Subscribe(CommandFlags flags) => _parent.Subscribe(Channel, HandleMessage, flags);
internal Task SubscribeAsync(CommandFlags flags) => _parent.SubscribeAsync(_redisChannel, HandleMessage, flags); internal Task SubscribeAsync(CommandFlags flags) => _parent.SubscribeAsync(Channel, HandleMessage, flags);
private void HandleMessage(RedisChannel channel, RedisValue value) private void HandleMessage(RedisChannel channel, RedisValue value)
{ {
var writer = _channel.Writer; var writer = _queue.Writer;
if (channel.IsNull && value.IsNull) // see ForSyncShutdown if (channel.IsNull && value.IsNull) // see ForSyncShutdown
{ {
writer.TryComplete(); writer.TryComplete();
} }
else else
{ {
writer.TryWrite(new ChannelMessage(channel, value)); writer.TryWrite(new ChannelMessage(this, channel, value));
} }
} }
...@@ -97,12 +107,12 @@ private void HandleMessage(RedisChannel channel, RedisValue value) ...@@ -97,12 +107,12 @@ private void HandleMessage(RedisChannel channel, RedisValue value)
/// Consume a message from the channel /// Consume a message from the channel
/// </summary> /// </summary>
public ValueTask<ChannelMessage> ReadAsync(CancellationToken cancellationToken = default) public ValueTask<ChannelMessage> ReadAsync(CancellationToken cancellationToken = default)
=> _channel.Reader.ReadAsync(cancellationToken); => _queue.Reader.ReadAsync(cancellationToken);
/// <summary> /// <summary>
/// Attempt to synchronously consume a message from the channel /// Attempt to synchronously consume a message from the channel
/// </summary> /// </summary>
public bool TryRead(out ChannelMessage item) => _channel.Reader.TryRead(out item); public bool TryRead(out ChannelMessage item) => _queue.Reader.TryRead(out item);
/// <summary> /// <summary>
/// Attempt to query the backlog length of the queue /// Attempt to query the backlog length of the queue
...@@ -112,10 +122,10 @@ public bool TryGetCount(out int count) ...@@ -112,10 +122,10 @@ public bool TryGetCount(out int count)
// get this using the reflection // get this using the reflection
try try
{ {
var prop = _channel.GetType().GetProperty("ItemsCountForDebugger", BindingFlags.Instance | BindingFlags.NonPublic); var prop = _queue.GetType().GetProperty("ItemsCountForDebugger", BindingFlags.Instance | BindingFlags.NonPublic);
if (prop != null) if (prop != null)
{ {
count = (int)prop.GetValue(_channel); count = (int)prop.GetValue(_queue);
return true; return true;
} }
} }
...@@ -134,16 +144,15 @@ private void AssertOnMessage(Delegate handler) ...@@ -134,16 +144,15 @@ private void AssertOnMessage(Delegate handler)
/// <summary> /// <summary>
/// Create a message loop that processes messages sequentially /// Create a message loop that processes messages sequentially
/// </summary> /// </summary>
public void OnMessage(Action<RedisChannel, RedisValue> handler) public void OnMessage(Action<ChannelMessage> handler)
{ {
AssertOnMessage(handler); AssertOnMessage(handler);
ThreadPool.QueueUserWorkItem( ThreadPool.QueueUserWorkItem(
state => ((ChannelMessageQueue)state).OnMessageSyncImpl(), this); state => ((ChannelMessageQueue)state).OnMessageSyncImpl(), this);
} }
private async void OnMessageSyncImpl() private async void OnMessageSyncImpl()
{ {
var handler = (Action<RedisChannel, RedisValue>)_onMessageHandler; var handler = (Action<ChannelMessage>)_onMessageHandler;
while (!IsCompleted) while (!IsCompleted)
{ {
ChannelMessage next; ChannelMessage next;
...@@ -155,7 +164,7 @@ private async void OnMessageSyncImpl() ...@@ -155,7 +164,7 @@ private async void OnMessageSyncImpl()
break; break;
} }
try { handler.Invoke(next.Channel, next.Value); } try { handler(next); }
catch { } // matches MessageCompletable catch { } // matches MessageCompletable
} }
} }
...@@ -163,7 +172,7 @@ private async void OnMessageSyncImpl() ...@@ -163,7 +172,7 @@ private async void OnMessageSyncImpl()
/// <summary> /// <summary>
/// Create a message loop that processes messages sequentially /// Create a message loop that processes messages sequentially
/// </summary> /// </summary>
public void OnMessage(Func<RedisChannel, RedisValue, Task> handler) public void OnMessage(Func<ChannelMessage, Task> handler)
{ {
AssertOnMessage(handler); AssertOnMessage(handler);
ThreadPool.QueueUserWorkItem( ThreadPool.QueueUserWorkItem(
...@@ -172,7 +181,7 @@ public void OnMessage(Func<RedisChannel, RedisValue, Task> handler) ...@@ -172,7 +181,7 @@ public void OnMessage(Func<RedisChannel, RedisValue, Task> handler)
private async void OnMessageAsyncImpl() private async void OnMessageAsyncImpl()
{ {
var handler = (Func<RedisChannel, RedisValue, Task>)_onMessageHandler; var handler = (Func<ChannelMessage, Task>)_onMessageHandler;
while (!IsCompleted) while (!IsCompleted)
{ {
ChannelMessage next; ChannelMessage next;
...@@ -186,8 +195,8 @@ private async void OnMessageAsyncImpl() ...@@ -186,8 +195,8 @@ private async void OnMessageAsyncImpl()
try try
{ {
var task = handler.Invoke(next.Channel, next.Value); var task = handler(next);
if (task != null) await task.ConfigureAwait(false); if (task != null && task.Status != TaskStatus.RanToCompletion) await task.ConfigureAwait(false);
} }
catch { } // matches MessageCompletable catch { } // matches MessageCompletable
} }
...@@ -197,9 +206,9 @@ internal void UnsubscribeImpl(Exception error = null, CommandFlags flags = Comma ...@@ -197,9 +206,9 @@ internal void UnsubscribeImpl(Exception error = null, CommandFlags flags = Comma
var parent = _parent; var parent = _parent;
if (parent != null) if (parent != null)
{ {
parent.UnsubscribeAsync(_redisChannel, HandleMessage, flags); parent.UnsubscribeAsync(Channel, HandleMessage, flags);
_parent = null; _parent = null;
_channel.Writer.TryComplete(error); _queue.Writer.TryComplete(error);
} }
} }
internal async Task UnsubscribeAsyncImpl(Exception error = null, CommandFlags flags = CommandFlags.None) internal async Task UnsubscribeAsyncImpl(Exception error = null, CommandFlags flags = CommandFlags.None)
...@@ -207,9 +216,9 @@ internal async Task UnsubscribeAsyncImpl(Exception error = null, CommandFlags fl ...@@ -207,9 +216,9 @@ internal async Task UnsubscribeAsyncImpl(Exception error = null, CommandFlags fl
var parent = _parent; var parent = _parent;
if (parent != null) if (parent != null)
{ {
await parent.UnsubscribeAsync(_redisChannel, HandleMessage, flags).ConfigureAwait(false); await parent.UnsubscribeAsync(Channel, HandleMessage, flags).ConfigureAwait(false);
_parent = null; _parent = null;
_channel.Writer.TryComplete(error); _queue.Writer.TryComplete(error);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment