Пример #1
0
        private async Task<bool> Invoke(MessageResult result, Action<Subscription, object> beforeInvoke, object state)
        {
            // Change the state from idle to invoking callback
            var prevState = Interlocked.CompareExchange(ref _subscriptionState,
                                                        SubscriptionState.InvokingCallback,
                                                        SubscriptionState.Idle);

            if (prevState == SubscriptionState.Disposed)
            {
                // Only allow terminal messages after dispose
                if (!result.Terminal)
                {
                    return false;
                }
            }

            beforeInvoke(this, state);

            _counters.MessageBusMessagesReceivedTotal.IncrementBy(result.TotalCount);
            _counters.MessageBusMessagesReceivedPerSec.IncrementBy(result.TotalCount);

            try
            {
                return await _callback(result, _callbackState);
            }
            finally
            {
                // Go from invoking callback to idle
                Interlocked.CompareExchange(ref _subscriptionState,
                                            SubscriptionState.Idle,
                                            SubscriptionState.InvokingCallback);
            }
        }
Пример #2
0
        private Task<bool> Invoke(MessageResult result, Action<object> beforeInvoke, object state)
        {
            // Change the state from idle to invoking callback
            var prevState = Interlocked.CompareExchange(ref _subscriptionState,
                                                        SubscriptionState.InvokingCallback,
                                                        SubscriptionState.Idle);

            if (prevState == SubscriptionState.Disposed)
            {
                // Only allow terminal messages after dispose
                if (!result.Terminal)
                {
                    return TaskAsyncHelper.False;
                }
            }

            beforeInvoke(state);

            _counters.MessageBusMessagesReceivedTotal.IncrementBy(result.TotalCount);
            _counters.MessageBusMessagesReceivedPerSec.IncrementBy(result.TotalCount);

            return _callback.Invoke(result, _callbackState).ContinueWith(task =>
            {
                // Go from invoking callback to idle
                Interlocked.CompareExchange(ref _subscriptionState,
                                            SubscriptionState.Idle,
                                            SubscriptionState.InvokingCallback);
                return task;
            },
            TaskContinuationOptions.ExecuteSynchronously).FastUnwrap();
        }
Пример #3
0
        private Task<bool> TriggerAcks(MessageResult result, object state)
        {
            result.Messages.Enumerate<object>(m => m.IsAck,
                                              (s, m) => ((IAckHandler)s).TriggerAck(m.CommandId),
                                              state: _ackHandler);

            return TaskAsyncHelper.True;
        }
Пример #4
0
        private void WorkImpl(TaskCompletionSource<object> taskCompletionSource)
        {
        Process:
            if (!Alive)
            {
                // If this subscription is dead then return immediately
                taskCompletionSource.TrySetResult(null);
                return;
            }

            var items = new List<ArraySegment<Message>>();
            int totalCount;
            object state;

            PerformWork(items, out totalCount, out state);

            if (items.Count > 0)
            {
                var messageResult = new MessageResult(items, totalCount);
                Task<bool> callbackTask = Invoke(messageResult, s => BeforeInvoke(s), state);

                if (callbackTask.IsCompleted)
                {
                    try
                    {
                        // Make sure exceptions propagate
                        callbackTask.Wait();

                        if (callbackTask.Result)
                        {
                            // Sync path
                            goto Process;
                        }
                        else
                        {
                            // If we're done pumping messages through to this subscription
                            // then dispose
                            Dispose();

                            // If the callback said it's done then stop
                            taskCompletionSource.TrySetResult(null);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ex.InnerException is TaskCanceledException)
                        {
                            taskCompletionSource.TrySetCanceled();
                        }
                        else
                        {
                            taskCompletionSource.TrySetUnwrappedException(ex);
                        }
                    }
                }
                else
                {
                    WorkImplAsync(callbackTask, taskCompletionSource);
                }
            }
            else
            {
                taskCompletionSource.TrySetResult(null);
            }
        }
Пример #5
0
            public Task<bool> InvokeCallback(MessageResult result)
            {
                var response = _connection.GetResponse(result);

                return _callback(response, _callbackState);
            }
Пример #6
0
        private PersistentResponse GetResponse(MessageResult result)
        {
            // Do a single sweep through the results to process commands and extract values
            ProcessResults(result);

            Debug.Assert(WriteCursor != null, "Unable to resolve the cursor since the method is null");

            var response = new PersistentResponse(ExcludeMessage, WriteCursor);
            response.Terminal = result.Terminal;

            if (!result.Terminal)
            {
                // Only set these properties if the message isn't terminal
                response.Messages = result.Messages;
                response.Disconnect = _disconnected;
                response.Aborted = _aborted;
                response.TotalCount = result.TotalCount;
            }

            PopulateResponseState(response);

            _counters.ConnectionMessagesReceivedTotal.IncrementBy(result.TotalCount);
            _counters.ConnectionMessagesReceivedPerSec.IncrementBy(result.TotalCount);

            return response;
        }
Пример #7
0
        private void ProcessResults(MessageResult result)
        {
            result.Messages.Enumerate<object>(message => message.IsAck || message.IsCommand,
                                              (state, message) =>
                                              {
                                                  if (message.IsAck)
                                                  {
                                                      _ackHandler.TriggerAck(message.CommandId);
                                                  }
                                                  else if (message.IsCommand)
                                                  {
                                                      var command = _serializer.Parse<Command>(message.Value, message.Encoding);

                                                      if (command == null)
                                                      {
                                                          if (MonoUtility.IsRunningMono)
                                                          {
                                                              return;
                                                          }

                                                          throw new SerializationException("Couldn't parse message " + message.Value);
                                                      }

                                                      ProcessCommand(command);

                                                      // Only send the ack if this command is waiting for it
                                                      if (message.WaitForAck)
                                                      {
                                                          // If we're on the same box and there's a pending ack for this command then
                                                          // just trip it
                                                          if (!_ackHandler.TriggerAck(message.CommandId))
                                                          {
                                                              _bus.Ack(_connectionId, message.CommandId).Catch();
                                                          }
                                                      }
                                                  }
                                              }, null);
        }
Пример #8
0
 public virtual Task<bool> Invoke(MessageResult result)
 {
     return Invoke(result, () => { });
 }
Пример #9
0
        private static Task<bool> MessageBusCallback(MessageResult result, object state)
        {
            var context = (ReceiveContext)state;

            return context.InvokeCallback(result);
        }
Пример #10
0
        public async Task Work()
        {
            // Set the state to working
            Interlocked.Exchange(ref _state, State.Working);

            while (Alive)
            {
                var items = new List<ArraySegment<Message>>();
                int totalCount;
                object state;

                PerformWork(items, out totalCount, out state);

                if (items.Count > 0)
                {
                    var messageResult = new MessageResult(items, totalCount);

                    bool result = await Invoke(messageResult, (s, o) => s.BeforeInvoke(o), state);

                    if (!result)
                    {
                        Dispose();

                        // If the callback said it's done then stop
                        break;
                    }
                }
                else
                {
                    break;
                }
            }
        }
Пример #11
0
        private void WorkImpl(TaskCompletionSource <object> taskCompletionSource)
        {
Process:
            if (!Alive)
            {
                // If this subscription is dead then return immediately
                taskCompletionSource.TrySetResult(null);
                return;
            }

            var    items = new List <ArraySegment <Message> >();
            int    totalCount;
            object state;

            PerformWork(items, out totalCount, out state);

            if (items.Count > 0)
            {
                var         messageResult = new MessageResult(items, totalCount);
                Task <bool> callbackTask  = Invoke(messageResult, s => BeforeInvoke(s), state);

                if (callbackTask.IsCompleted)
                {
                    try
                    {
                        // Make sure exceptions propagate
                        callbackTask.Wait();

                        if (callbackTask.Result)
                        {
                            // Sync path
                            goto Process;
                        }
                        else
                        {
                            // If we're done pumping messages through to this subscription
                            // then dispose
                            Dispose();

                            // If the callback said it's done then stop
                            taskCompletionSource.TrySetResult(null);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ex.InnerException is TaskCanceledException)
                        {
                            taskCompletionSource.TrySetCanceled();
                        }
                        else
                        {
                            taskCompletionSource.TrySetUnwrappedException(ex);
                        }
                    }
                }
                else
                {
                    WorkImplAsync(callbackTask, taskCompletionSource);
                }
            }
            else
            {
                taskCompletionSource.TrySetResult(null);
            }
        }
Пример #12
0
        private PersistentResponse GetResponse(MessageResult result)
        {
            // Do a single sweep through the results to process commands and extract values
            ProcessResults(result);

            Debug.Assert(GetCursor != null, "Unable to resolve the cursor since the method is null");

            // Resolve the cursor
            string id = GetCursor();

            var response = new PersistentResponse(ExcludeMessage)
            {
                MessageId = id,
                Messages = result.Messages,
                Disconnect = _disconnected,
                Aborted = _aborted,
                TotalCount = result.TotalCount,
            };

            PopulateResponseState(response);

            _counters.ConnectionMessagesReceivedTotal.IncrementBy(result.TotalCount);
            _counters.ConnectionMessagesReceivedPerSec.IncrementBy(result.TotalCount);

            return response;
        }
Пример #13
0
        private void ProcessResults(MessageResult result)
        {
            result.Messages.Enumerate(message => message.IsAck || message.IsCommand,
                                      message =>
                                      {
                                          if (message.IsAck)
                                          {
                                              _ackHandler.TriggerAck(message.CommandId);
                                          }
                                          else if (message.IsCommand)
                                          {
                                              var command = _serializer.Parse<Command>(message.Value);
                                              ProcessCommand(command);

                                              // Only send the ack if this command is waiting for it
                                              if (message.WaitForAck)
                                              {
                                                  // If we're on the same box and there's a pending ack for this command then
                                                  // just trip it
                                                  if (!_ackHandler.TriggerAck(message.CommandId))
                                                  {
                                                      _bus.Ack(_connectionId, message.CommandId).Catch();
                                                  }
                                              }
                                          }
                                      });
        }
        private Task<bool> HandleServerCommands(MessageResult result, object state)
        {
            result.Messages.Enumerate<object>(m => ServerSignal.Equals(m.Key),
                                              (s, m) =>
                                              {
                                                  var command = _serializer.Parse<ServerCommand>(m.Value, m.Encoding);
                                                  OnCommand(command);
                                              },
                                              state: null);

            return TaskAsyncHelper.True;
        }
Пример #15
0
 private void ProcessResults(MessageResult result)
 {
     result.Messages.Enumerate<Connection>(message => message.IsCommand,
         (connection, message) => ProcessResultsCore(connection, message), this);
 }
Пример #16
0
        private Task<bool> Invoke(MessageResult result, Action beforeInvoke)
        {
            // Change the state from idle to invoking callback
            var state = Interlocked.CompareExchange(ref _subscriptionState,
                                                    SubscriptionState.InvokingCallback,
                                                    SubscriptionState.Idle);

            if (state == SubscriptionState.Disposed)
            {
                // Only allow terminal messages after dispose
                if (!result.Terminal)
                {
                    return TaskAsyncHelper.False;
                }
            }

            beforeInvoke();

            return _callback.Invoke(result).ContinueWith(task =>
            {
                // Go from invoking callback to idle
                Interlocked.CompareExchange(ref _subscriptionState,
                                            SubscriptionState.Idle,
                                            SubscriptionState.InvokingCallback);

                if (task.IsFaulted)
                {
                    return TaskAsyncHelper.FromError<bool>(task.Exception);
                }

                return TaskAsyncHelper.FromResult(task.Result);
            },
            TaskContinuationOptions.ExecuteSynchronously).FastUnwrap();
        }
Пример #17
0
 public virtual Task<bool> Invoke(MessageResult result)
 {
     return Invoke(result, state => { }, state: null);
 }
Пример #18
0
        public void AcksAreSentToAckSubscriber()
        {
            // Arrange
            var waitCommand = new Command { WaitForAck = true };
            var ackerId = "acker";
            var waiterId = "waiter";
            var messageId = "messageId";
            var maxMessages = 1;

            var ackHandler = new Mock<IAckHandler>();
            ackHandler.Setup(m => m.TriggerAck(waitCommand.Id)).Returns(false);

            var messageBus = new Mock<IMessageBus>();
            Message waitMessage = null;
            Message ackMessage = null;
            messageBus.Setup(m => m.Publish(It.IsAny<Message>())).Returns<Message>(m =>
            {
                if (m.WaitForAck)
                {
                    waitMessage = m;
                }
                else if (m.IsAck)
                {
                    ackMessage = m;
                }

                return TaskAsyncHelper.Empty;
            });

            var loggerFactory = new Mock<ILoggerFactory>();
            var counters = new PerformanceCounterManager(loggerFactory.Object);

            var serializer = JsonUtility.CreateDefaultSerializer();
            var waiterConnection = new Connection(messageBus.Object,
                                                  serializer,
                                                  "signal",
                                                  waiterId,
                                                  new string[] { },
                                                  new string[] { },
                                                  loggerFactory.Object,
                                                  ackHandler.Object,
                                                  counters,
                                                  new Mock<IProtectedData>().Object,
                                                  new MemoryPool());

            // Act
            waiterConnection.Send(ackerId, waitCommand);

            // Assert
            Assert.NotNull(waitMessage);
            Assert.Equal(waiterId, waitMessage.Source);
            Assert.Equal(PrefixHelper.GetConnectionId(ackerId), waitMessage.Key);

            // Arrange some more now that we have a waitMessage
            var messages = new List<ArraySegment<Message>>()
            {
                new ArraySegment<Message>(new[] { waitMessage })
            };
            var messageResult = new MessageResult(messages, 1);

            var ackerConnection = new Connection(messageBus.Object,
                                                 serializer,
                                                 "signal",
                                                 ackerId,
                                                 new string[] { },
                                                 new string[] { },
                                                 loggerFactory.Object,
                                                 ackHandler.Object,
                                                 counters,
                                                 new Mock<IProtectedData>().Object,
                                                 new Mock<IMemoryPool>().Object);
            ackerConnection.WriteCursor = _ => { };

            messageBus.Setup(m => m.Subscribe(ackerConnection,
                                              messageId,
                                              It.IsAny<Func<MessageResult, object, Task<bool>>>(),
                                              maxMessages,
                                              It.IsAny<object>()))
                .Callback<ISubscriber,
                          string,
                          Func<MessageResult, object, Task<bool>>,
                          int,
                          object>((subsciber, cursor, callback, max, state) =>
                {
                    callback(messageResult, state);
                });

            // Act
            ackerConnection.Receive(messageId, (_, __) => TaskAsyncHelper.False, maxMessages, null);

            // Assert
            Assert.NotNull(ackMessage);
            Assert.Equal(ackerId, ackMessage.Source);
            Assert.Equal(AckSubscriber.Signal, ackMessage.Key);
        }
Пример #19
0
        private Task<bool> HandleServerCommands(MessageResult result)
        {
            result.Messages.Enumerate(m => ServerSignal.Equals(m.Key),
                                      m =>
                                      {
                                          var command = _serializer.Parse<ServerCommand>(m.Value);
                                          OnCommand(command);
                                      });

            return TaskAsyncHelper.True;
        }
Пример #20
0
 public virtual Task <bool> Invoke(MessageResult result)
 {
     return(Invoke(result, (s, o) => { }, state: null));
 }