Beispiel #1
0
        public bool ScheduleReceive(Action <bool> whenDone)
        {
            CoreEventSource.ReceiveStart(name, IsAlive);

            if (!IsAlive)
            {
                whenDone(false);
                return(false);
            }

            if (Interlocked.CompareExchange(ref isReceiving, 1, 0) != 0)
            {
                return(false);
            }

            recvArgs.UserToken = whenDone;

            if (!socket.ReceiveAsync(recvArgs))
            {
                // receive was done synchronously
                ReceiveAsyncCompleted(null, recvArgs);
            }

            return(true);
        }
Beispiel #2
0
        private void SendAsyncCompleted(object sender, SocketAsyncEventArgs e)
        {
            var sent = sendArgs.BytesTransferred;

            CoreEventSource.SendChunk(name, IsAlive, sent, sendArgs.SocketError);

            // failed during send
            if (sendArgs.SocketError != SocketError.Success || sent < 1)
            {
                FinishSending(false);
                return;
            }

            var sendOffset = sendArgs.Offset + sent;
            var sendCount  = sendArgs.Count - sent;

            // OS sent less data than we asked for, so send the remaining data
            if (sendCount > 0)
            {
                PerformSend(sendOffset, sendCount);
            }
            else
            {
                Debug.Assert(sendCount == 0);

                // all data was sent
                sendBuffer.Clear();
                FinishSending(true);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Writes an operation to the output buffer. Handles the case where the op does not fit the buffer fully.
        /// </summary>
        /// <param name="data"></param>
        protected virtual void WriteOp(OpQueueEntry data)
        {
            if (currentWriteCopier != null)
            {
                throw new InvalidOperationException("Cannot write an operation while another is in progress.");
            }

            var request = data.Op.CreateRequest();

            if (!request.WriteTo(socket.WriteBuffer))             // no pending IO => fully written
            {
                readQueue.Enqueue(data);
                request.Dispose();

                #region Diagnostics
                npm.EnqueueReadOp();
                CoreEventSource.EnqueueReadOp(name);

                LogTo.Trace($"Full send of {data.Op}");
                #endregion
            }
            else
            {
                // it did not fit into the write buffer, so save the current op
                // as "in-progress"; DoRun will loop until it's fully sent
                currentWriteOp     = data;
                currentWriteCopier = request;

                LogTo.Trace($"Partial send of {data.Op}");
            }
        }
Beispiel #4
0
        /// <summary>
        /// Sends the current chunked op. Happens when an op's data cannot fit the write buffer in one pass.
        /// </summary>
        /// <returns>returns true if further IO is required; false if no inprogress op present or the last chunk was successfully added to the buffer</returns>
        private bool ContinueWritingCurrentOp()
        {
            // check if we have an op in progress
            if (currentWriteCopier == null)
            {
                return(false);
            }
            if (currentWriteCopier.WriteTo(socket.WriteBuffer))
            {
                return(true);
            }

            // last chunk was sent
            LogTo.Trace($"Sent & finished {currentWriteOp.Op}");

            // op is sent fully; response can be expected
            readQueue.Enqueue(currentWriteOp);

            #region Diagnostics
            npm.EnqueueReadOp();
            CoreEventSource.EnqueueReadOp(name);
            #endregion

            // clean up
            currentWriteCopier.Dispose();
            currentWriteCopier = null;
            currentWriteOp     = OpQueueEntry.Empty;

            return(false);
        }
Beispiel #5
0
        private void FinishSending(bool success)
        {
            CoreEventSource.SendStop(name, IsAlive, success);

            Volatile.Write(ref isSending, 0);

            ((Action <bool>)sendArgs.UserToken)?.Invoke(success);
        }
Beispiel #6
0
        private void ReceiveAsyncCompleted(object sender, SocketAsyncEventArgs recvArgs)
        {
            var received = recvArgs.BytesTransferred;

            CoreEventSource.ReceiveChunk(name, IsAlive, received, recvArgs.SocketError);

            var success = recvArgs.SocketError == SocketError.Success && received > 0;

            recvBuffer.SetDataLength(success ? received : 0);

            CoreEventSource.ReceiveStop(name, IsAlive, success);
            Volatile.Write(ref isReceiving, 0);

            ((Action <bool>)recvArgs.UserToken).Invoke(success);
        }
Beispiel #7
0
        /// <summary>
        /// Cleans up a ConcurrentQueue, marking all items as failed
        /// </summary>
        private void FailQueue(ConcurrentQueue <OpQueueEntry> queue, Exception e)
        {
            OpQueueEntry data;

            while (queue.TryDequeue(out data))
            {
                var t = data.Task;
                if (t != null)
                {
                    t.SetException(e);
                }

                npm.Error();
                CoreEventSource.NodeError(name);
            }
        }
Beispiel #8
0
        protected virtual OpQueueEntry GetNextOp()
        {
            OpQueueEntry data;

            if (writeQueue.TryDequeue(out data))
            {
                #region Diagnostics
                npm.DequeueWriteOp();
                CoreEventSource.DequeueWriteOp(name);
                #endregion

                return(data);
            }

            return(OpQueueEntry.Empty);
        }
Beispiel #9
0
        /// <summary>
        /// Cleans up an AdvQueue, marking all items as failed
        /// </summary>
        private void FailQueue(Queue <OpQueueEntry> queue, Exception e)
        {
            foreach (var data in queue)
            {
                var t = data.Task;
                if (t == null)
                {
                    break;
                }
                t.TrySetException(e);

                npm.Error();
                CoreEventSource.NodeError(name);
            }

            queue.Clear();
        }
Beispiel #10
0
        private void PerformConnect(IPEndPoint endpoint, CancellationToken token)
        {
            this.endpoint = endpoint;
            this.name     = endpoint.ToString();
            this.IsAlive  = false;

            var sw = Stopwatch.StartNew();

            InitBuffers();

            using (var mre = new ManualResetEventSlim(false))
                using (var opt = new SocketAsyncEventArgs {
                    RemoteEndPoint = endpoint
                })
                {
                    CoreEventSource.ConnectStart(name);

                    opt.Completed += (a, b) => mre.Set();
                    RecreateSocket();

                    try
                    {
                        if (socket.ConnectAsync(opt) &&
                            !mre.Wait((int)ConnectionTimeout.TotalMilliseconds, token))
                        {
                            CoreEventSource.ConnectFail(name, SocketError.TimedOut);
                            Socket.CancelConnectAsync(opt);
                            throw new TimeoutException($"Connection timeout {ConnectionTimeout} has been exceeded while trying to connect to {endpoint}");
                        }

                        if (opt.SocketError != SocketError.Success)
                        {
                            CoreEventSource.ConnectFail(name, opt.SocketError);
                            throw new IOException($"Could not connect to {endpoint}");
                        }

                        IsAlive = true;
                    }
                    finally
                    {
                        LogTo.Info($"Connected to {endpoint} in {sw.ElapsedMilliseconds} msec");
                    }
                }
        }
Beispiel #11
0
        public bool ScheduleSend(Action <bool> whenDone)
        {
            CoreEventSource.SendStart(name, IsAlive, sendBuffer.Position);

            if (!IsAlive)
            {
                whenDone(false);
                return(false);
            }

            if (Interlocked.CompareExchange(ref isSending, 1, 0) != 0)
            {
                return(false);
            }

            sendArgs.UserToken = whenDone;
            PerformSend(sendBuffer.BufferOffset, sendBuffer.Position);

            return(true);
        }
Beispiel #12
0
        private void PerformSend(int sendOffset, int sendCount)
        {
            for (;;)
            {
                // try sending all our data
                sendArgs.SetBuffer(sendOffset, sendCount);
                // send is being done asynchrously (SendAsyncCompleted will clean up)
                if (socket.SendAsync(sendArgs))
                {
                    break;
                }

                // send was done synchronously
                var sent = sendArgs.BytesTransferred;
                CoreEventSource.SendChunk(name, IsAlive, sent, sendArgs.SocketError);

                // check for fail
                if (sendArgs.SocketError != SocketError.Success || sent < 1)
                {
                    // socket error
                    FinishSending(false);
                    break;
                }

                sendOffset += sent;
                sendCount  -= sent;

                Debug.Assert(sendCount >= 0);

                // no data is remaining: quit
                // otherwise try sending a new chunk
                if (sendCount == 0)
                {
                    sendBuffer.Clear();
                    FinishSending(true);
                    break;
                }
            }
        }
Beispiel #13
0
        public virtual Task <IOperation> Enqueue(IOperation op)
        {
            var tcs = new TaskCompletionSource <IOperation>();

            npm.NewOp();

            try
            {
                if (IsAlive)
                {
                    writeQueue.Enqueue(new OpQueueEntry(op, tcs));

                    #region Diagnostics
                    npm.EnqueueWriteOp();
                    CoreEventSource.EnqueueWriteOp(name);
                    #endregion
                }
                else
                {
                    tcs.TrySetException(new IOException(endpoint + " is not alive"));

                    #region Diagnostics
                    npm.Error();
                    CoreEventSource.NodeError(name);
                    #endregion
                }
            }
            catch (Exception e)
            {
                tcs.TrySetException(new IOException(endpoint + " enqueue failed. See inner excption for details.", e));

                #region Diagnostics
                npm.Error();
                CoreEventSource.NodeError(name);
                #endregion
            }

            return(tcs.Task);
        }
Beispiel #14
0
        private void TryProcessReceivedData()
        {
            // process the commands in the readQueue
            while (readQueue.Count > 0)
            {
                // continue filling the previously unfinished response,
                // or create a new one
                var response = inprogressResponse ?? CreateResponse();

                // continue filling the Response object from the buffer
                // Read() returns true if further data (IO) is required
                // (usually when the current response data is larger than the receive buffer size)
                if (response.Read(socket.ReadBuffer))
                {
                    inprogressResponse = response;
                    LogTo.Trace("Response is not read fully, continue reading from the socket.");

                    // refill the buffer
                    _FinishedReading();
                    owner.NeedsIO(this);

                    return;
                }

                // successfully read a response from the read buffer
                inprogressResponse = null;
                var isHandled = false;

                while (!isHandled && readQueue.Count > 0)
                {
                    var data = readQueue.Peek();
                    Debug.Assert(!data.IsEmpty);

                    // If the response does not matches the current op, it means it's a
                    // response to a later command in the queue, so all commands before it
                    // were silent commands without a response (== usually success).
                    // So, successful silent ops will receive null as response (since
                    // we have no real response (or we've ran into a bug))
                    isHandled = data.Op.Handles(response);
                    LogTo.Trace($"Command {data.Op} handles reponse: {isHandled}");

                    // operations are allowed to hanndle subsequent responses
                    // they returns false when no more IO (response) is required => done processing
                    if (!data.Op.ProcessResponse(isHandled ? response : null))
                    {
                        readQueue.Dequeue();
                        data.Task.TrySetResult(data.Op);

                        #region Diagnostics
                        npm.DequeueReadOp();
                        CoreEventSource.DequeueReadOp(name);
                        #endregion
                    }
                }

                response.Dispose();
            }

            LogTo.Trace($"{name} fininshed RECEIVE, unlock read");
            _FinishedReading();
        }