public void Publish(Topic topic, string message)
        {
            TopicState topicState;

            if (topics.TryGetValue(topic, out topicState))
            {
                topicState.Publish(message);
            }
        }
        public void Subscribe(string client, Topic topic, Action<Topic, string> handler)
        {
            var clientState = clients.GetOrAdd(client, _ => new ClientState(client));
            var topicState = topics.GetOrAdd(topic, _ => new TopicState(topic));

            lock (syncRoot)
            {
                topics[topic].Add(client, handler);
            }
        }
        public IEnumerable<string> GetClientsForTopic(Topic topic)
        {
            if (!topics.ContainsKey(topic))
                return new HashSet<string>();

            lock (syncRoot)
            {
                return topics[topic].Clients;
            }
        }
        public void Unsubscribe(string clientId, Topic topic)
        {
            TopicState topicState;

            if (topics.TryGetValue(topic, out topicState))
            {
                lock (syncRoot)
                {
                    topicState.Remove(clientId);

                    /* This is the last subscriber on this topic, remove the topic. */
                    if (topicState.Clients.Count() == 0)
                        topics.TryRemove(topic, out topicState);

                    if (topicState.Clients.Count() != 0)
                        throw new InvalidOperationException("Subscribed topic was removed.");
                }
            }
        }
 public TopicState(Topic topic)
 {
     this.topic = topic;
     clients = new Dictionary<string, Action<Topic, string>>(StringComparer.OrdinalIgnoreCase);
 }