Task c_MessageReceived(Turbocharged.NSQ.Message obj)
        {
            var data    = BitConverter.ToString(obj.Body, 0);
            var message = string.Format("RECEIVED MESSAGE. Id = {0}, Msg = {1}", obj.Id, data);

            PostMessage(message);
            return(Task.FromResult(0));
        }
        void WorkerLoop(object messageHandler)
        {
            MessageHandler handler = (MessageHandler)messageHandler;
            bool firstConnectionAttempt = true;
            TcpClient client = null;
            FrameReader reader = null;
            IBackoffLimiter backoffLimiter = null;
            IDisposable cancellationRegistration = Disposable.Empty;

            while (true)
            {
                try
                {
                    if (_connectionClosedSource.IsCancellationRequested)
                    {
                        return;
                    }

                    if (!Connected)
                    {
                        lock (_connectionSwapLock)
                        {
                            if (firstConnectionAttempt)
                            {
                                firstConnectionAttempt = false;
                            }
                            else
                            {
                                if (backoffLimiter == null)
                                    backoffLimiter = _backoffStrategy.Create();

                                TimeSpan delay;
                                if (backoffLimiter.ShouldReconnect(out delay))
                                {
                                    OnInternalMessage("Delaying {0} ms before reconnecting", (int)delay.TotalMilliseconds);
                                    Thread.Sleep(delay);
                                }
                                else
                                {
                                    // We give up
                                    OnInternalMessage("Abandoning connection");
                                    Dispose();
                                    return;
                                }
                            }

                            lock (_connectionSwapInProgressLock)
                            {
                                CancellationToken cancellationToken;
                                lock (_disposeLock)
                                {
                                    if (_disposed) return;

                                    if (client != null)
                                    {
                                        cancellationRegistration.Dispose();
                                        ((IDisposable)client).Dispose();
                                    }

                                    cancellationToken = _connectionClosedSource.Token;
                                }

                                OnInternalMessage("TCP client starting");
                                client = new TcpClient(_endPoint.Host, _endPoint.Port);
                                cancellationRegistration = cancellationToken.Register(() => ((IDisposable)client).Dispose(), false);
                                Connected = true;
                                OnInternalMessage("TCP client started");

                                _stream = client.GetStream();
                                reader = new FrameReader(_stream);

                                Handshake(_stream, reader);

                                _firstConnection.TrySetResult(true);

                                // Start a new backoff cycle next time we disconnect
                                backoffLimiter = null;

                                _nextReconnectionTaskSource.SetResult(true);
                                _nextReconnectionTaskSource = new TaskCompletionSource<bool>();
                            }
                        }
                    }

                    Frame frame;
                    while ((frame = reader.ReadFrame()) != null)
                    {
                        if (frame.Type == FrameType.Result)
                        {
                            if (HEARTBEAT.SequenceEqual(frame.Data))
                            {
                                OnInternalMessage("Heartbeat");
                                SendCommandAsync(new Nop())
                                    .ContinueWith(t => Dispose(), TaskContinuationOptions.OnlyOnFaulted);
                            }
                            else
                            {
                                OnInternalMessage("Received result. Length = {0}", frame.MessageSize);
                            }
                        }
                        else if (frame.Type == FrameType.Message)
                        {
                            OnInternalMessage("Received message. Length = {0}", frame.MessageSize);
                            var message = new Message(frame, this);
                            // TODO: Rethink this
                            ThreadPool.QueueUserWorkItem(new WaitCallback(_ => { handler(message); }));
                        }
                        else if (frame.Type == FrameType.Error)
                        {
                            string errorString;
                            try
                            {
                                errorString = Encoding.ASCII.GetString(frame.Data);
                            }
                            catch
                            {
                                errorString = BitConverter.ToString(frame.Data);
                            }
                            OnInternalMessage("Received error. Message = {0}", errorString);
                        }
                        else
                        {
                            OnInternalMessage("Unknown message type: {0}", frame.Type);
                            throw new InvalidOperationException("Unknown message type " + frame.Type);
                        }
                    }
                }
                catch (ObjectDisposedException ex)
                {
                    OnInternalMessage("Exiting worker loop due to disposal. Message = {0}", ex.Message);
                    Connected = false;
                    return;
                }
                catch (IOException ex)
                {
                    if (!_disposed) OnInternalMessage("EXCEPTION: {0}", ex.Message);
                    Connected = false;
                    continue;
                }
                catch (SocketException ex)
                {
                    if (!_disposed) OnInternalMessage("EXCEPTION: {0}", ex.Message);
                    Connected = false;
                    continue;
                }
            }
        }