public IAsyncResult BeginAdd(Message msg, ulong id, AsyncCallback cb, object state)
            {
                try
                {
                    this.rwLock.EnterWriteLock();
                    this.PurgeExpired();

                    var cmsg = new CachedMessage(msg.SignalKey, msg.Value, msg.Created, id);
                    if (this.messageIndex.Count + 1 < CapacityLimit)
                    {
                        // add and complete
                        this.AddToCache(cmsg);
                        return new CompletedAsyncResult(cb, state);
                    }
                    else
                    {
                        // move the 'Add' operation to pending until we've got room to 
                        // add the item and complete once the item has been dequeued
                        // calling Complete on the AR
                        var addAsyncResult = new AddAsyncResult(cb, state);
                        this.pendingAdds.EnqueueAndDispatch(cmsg, addAsyncResult.Complete);
                        return addAsyncResult;
                    }
                }
                finally
                {
                    this.rwLock.ExitWriteLock();
                }
            }
            public bool TryGetNext(IEnumerable<string> eventKeys, ref ushort hwmp, out Message msg)
            {
                this.PurgeExpired();

                msg = null;
                try
                {
                    this.rwLock.EnterReadLock();
                    if (this.messageIndex.Count > 0)
                    {
                        var hwm = (this.messageIndex.Keys[this.messageIndex.Count - 1] / 0x10000) * 0x10000 + hwmp;

                        // binary search to the right index
                        int i = 0, l = 0, r = this.messageIndex.Count - 1;
                        while (l <= r)
                        {
                            i = l + (r - l) / 2;
                            var v = this.messageIndex.Values[i].Id;
                            // check to see if value is equal to item in array
                            if (hwm == v)
                            {
                                break;
                            }
                            if (hwm < v)
                            {
                                r = i - 1;
                            }
                            else
                            {
                                l = i + 1;
                            }
                        }

                        if (i > l)
                        {
                            i = l;
                        }

                        // get the next message if there is one left
                        for (; i < this.messageIndex.Count; i++)
                        {
                            if (this.messageIndex.Values[i].Id <= hwm )
                            {
                                continue;
                            }
                            if (!eventKeys.Contains(this.messageIndex.Values[i].SignalKey))
                            {
                                hwmp = (ushort)(this.messageIndex.Values[i].Id & 0xffff);
                                continue;
                            }
                            var cmsg = this.messageIndex.Values[i];
                            msg = cmsg;
                            hwmp = (ushort)(cmsg.Id & 0xffff);
                            return true;
                        }
                    }
                }
                finally
                {
                    this.rwLock.ExitReadLock();
                }
                return false;
            }