public Task Send(string eventKey, object value) { var list = _cache.GetOrAdd(eventKey, _ => new LockedList<InMemoryMessage>()); InMemoryMessage message = null; try { // Take a write lock here so we ensure messages go into the list in order _cacheLock.EnterWriteLock(); // Only 1 save allowed at a time, to ensure messages are added to the list in order message = new InMemoryMessage(eventKey, value, GenerateId()); _trace.Source.TraceInformation("MessageBus: Saving message {0} with eventKey {1} to cache on AppDomain {2}", message.Id, eventKey, AppDomain.CurrentDomain.Id); list.AddWithLock(message); // Send to waiting callers. // This must be done in the read lock to ensure that messages are sent to waiting // connections in the order they were saved so that they always get the correct // last message id to resubscribe with. Broadcast(eventKey, message); } finally { _cacheLock.ExitWriteLock(); } return TaskAsyncHelper.Empty; }
private void Broadcast(string eventKey, InMemoryMessage message) { LockedList<Action<IList<InMemoryMessage>>> callbacks; if (_waitingTasks.TryGetValue(eventKey, out callbacks)) { var delegates = callbacks.CopyWithLock(); var messages = new[] { message }; if (delegates.Count == 0) { return; } _trace.Source.TraceInformation("MessageBus: Sending message {0} with eventKey {1} to {2} waiting connections", message.Id, eventKey, delegates.Count); foreach (var callback in delegates) { if (callback != null) { callback.Invoke(messages); } } } }