/// <summary>
        /// Subscribes the first observer to this subject and
        /// starts replaying/relaying signals. All future
        /// observers receive an InvalidOperationException.
        /// </summary>
        /// <param name="observer">The observer to subscribe with.</param>
        public void Subscribe(ISignalObserver <T> observer)
        {
            ValidationHelper.RequireNonNullRef(observer, nameof(observer));

            if (Interlocked.CompareExchange(ref once, 1, 0) == 0)
            {
                var inner = new MonocastDisposable(observer, this);
                observer.OnSubscribe(inner);
                if (Interlocked.CompareExchange(ref this.observer, inner, null) == null)
                {
                    if (inner.IsDisposed())
                    {
                        this.observer = null;
                    }
                    else
                    {
                        Drain();
                    }
                }
            }
            else
            {
                DisposableHelper.Error(observer, new InvalidOperationException("MonocastSubject allows at most one observer"));
            }
        }
        void DrainFused(MonocastDisposable observer)
        {
            var ex = Volatile.Read(ref error);

            if (!queue.IsEmpty())
            {
                observer.OnNext(default(T));
            }

            if (ex != null)
            {
                if (ex == ExceptionHelper.TERMINATED)
                {
                    observer.OnCompleted();
                }
                else
                {
                    observer.OnError(ex);
                }
                this.observer = null;
            }
        }
        void DrainNormal(MonocastDisposable observer)
        {
            for (; ;)
            {
                if (observer.IsDisposed())
                {
                    queue.Clear();
                    break;
                }
                else
                {
                    var ex    = Volatile.Read(ref error);
                    var v     = queue.TryPoll(out var success);
                    var empty = !success;

                    if (ex != null && empty)
                    {
                        if (ex == ExceptionHelper.TERMINATED)
                        {
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(ex);
                        }
                        this.observer = null;
                        break;
                    }

                    if (empty)
                    {
                        break;
                    }

                    observer.OnNext(v);
                }
            }
        }
 void Remove(MonocastDisposable inner)
 {
     Interlocked.CompareExchange(ref observer, null, inner);
     Terminate();
     Dispose();
 }