예제 #1
0
        private void OnRead(UvStreamHandle handle, int status)
        {
            if (status == 0)
            {
                // A zero status does not indicate an error or connection end. It indicates
                // there is no data to be read right now.
                // See the note at http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb.
                // We need to clean up whatever was allocated by OnAlloc.
                _inputBuffer.FlushAsync();
                return;
            }

            var normalRead = status > 0;
            var normalDone = status == EOF;
            var errorDone  = !(normalDone || normalRead);
            var readCount  = normalRead ? status : 0;

            if (!normalRead)
            {
                handle.ReadStop();
            }

            IOException error = null;

            if (errorDone)
            {
                Exception uvError;
                handle.Libuv.Check(status, out uvError);
                error = new IOException(uvError.Message, uvError);

                // REVIEW: Should we treat ECONNRESET as an error?
                // Ignore the error for now
                _input.CompleteWriter();
            }
            else if (readCount == 0 || _input.Writing.IsCompleted)
            {
                _input.CompleteWriter();
            }
            else
            {
                _inputBuffer.Advance(readCount);

                var task = _inputBuffer.FlushAsync();

                if (!task.IsCompleted)
                {
                    // If there's back pressure
                    handle.ReadStop();

                    // Resume reading when task continues
                    task.ContinueWith((t, state) => ((UvTcpConnection)state).StartReading(), this);
                }
            }
        }
예제 #2
0
        public void ByteByByteTest()
        {
            WritableBuffer writableBuffer = default;

            for (int i = 1; i <= 1024 * 1024; i++)
            {
                writableBuffer = _pipe.Writer.Alloc(100);
                writableBuffer.Advance(1);
                writableBuffer.Commit();

                Assert.Equal(i, _pipe.Length);
            }

            writableBuffer.FlushAsync();

            for (int i = 1024 * 1024 - 1; i >= 0; i--)
            {
                var result   = _pipe.Reader.ReadAsync().GetResult();
                var consumed = result.Buffer.Slice(1).Start;

                Assert.Equal(i + 1, result.Buffer.Length);

                _pipe.Reader.Advance(consumed, consumed);

                Assert.Equal(i, _pipe.Length);
            }
        }
예제 #3
0
        private byte[] Read()
        {
            _buffer.FlushAsync().GetAwaiter().GetResult();
            var readResult = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
            var data       = readResult.Buffer.ToArray();

            _pipe.Reader.Advance(readResult.Buffer.End);
            return(data);
        }
예제 #4
0
        public static void RunSingleSegmentParser(int numberOfRequests, int concurrentConnections, byte[] requestPayload, WriteResponseDelegate writeResponse)
        {
            var factory  = new PipeFactory();
            var listener = new FakeListener(factory, concurrentConnections);

            listener.OnConnection(async connection =>
            {
                while (true)
                {
                    // Wait for data
                    var result           = await connection.Input.ReadAsync();
                    ReadableBuffer input = result.Buffer;

                    try
                    {
                        if (input.IsEmpty && result.IsCompleted)
                        {
                            // No more data
                            break;
                        }

                        var requestBuffer = input.First;

                        if (requestBuffer.Length != 492)
                        {
                            continue;
                        }
                        // Parse the input http request
                        WritableBuffer output = WriteResponse(writeResponse, connection, requestBuffer);
                        await output.FlushAsync();
                    }
                    catch (Exception e)
                    {
                        var istr = new Utf8String(input.First.Span).ToString();
                        Debug.WriteLine(e.Message);
                    }
                    finally
                    {
                        // Consume the input
                        connection.Input.Advance(input.End, input.End);
                    }
                }
            });

            var tasks = new Task[numberOfRequests];

            for (int i = 0; i < numberOfRequests; i++)
            {
                tasks[i] = listener.ExecuteRequestAsync(requestPayload);
            }

            Task.WaitAll(tasks);

            listener.Dispose();
            factory.Dispose();
        }
예제 #5
0
        private Task FlushAsync(WritableBuffer writableBuffer, long bytesWritten, CancellationToken cancellationToken)
        {
            var awaitable = writableBuffer.FlushAsync(cancellationToken);

            if (awaitable.IsCompleted)
            {
                // The flush task can't fail today
                return(Task.CompletedTask);
            }
            return(FlushAsyncAwaited(awaitable, bytesWritten, cancellationToken));
        }
예제 #6
0
        private static void RunServerForNode()
        {
            UvThread      uvThread = new UvThread();
            var           ip       = IPAddress.Any;
            UvTcpListener listener = new UvTcpListener(uvThread, new IPEndPoint(ip, port));

            listener.OnConnection(async connection =>
            {
                Interlocked.Increment(ref connectionCounter);
                var input  = connection.Input;
                var output = connection.Output;
                var flag   = false;

                //Used for stop sending info to connected client.
                await Task.Factory.StartNew(async() =>
                {
                    //Wait for client disconnection.
                    var result = await input.ReadAsync();
                    flag       = true;
                });

                while (!flag)
                {
                    try
                    {
                        WritableBuffer oBuffer = output.Alloc();
                        oBuffer.WriteUtf8String(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:ms"));
                        await oBuffer.FlushAsync();
                        Interlocked.Increment(ref sendCounter);
                        await Task.Delay(r.Next(0, 500));
                    }
                    catch (Exception e)
                    {
                        break;
                    }
                }
            });

            listener.Start();

            var pid = System.Diagnostics.Process.GetCurrentProcess().Id;

            Console.WriteLine($"Listening on {ip} on port {port} / PID {pid}");
            Console.ReadKey();

            listener.Stop();
            uvThread.Dispose();
        }
예제 #7
0
        public void Complete(int status, long requestCorrelation, uint bytesTransferred)
        {
            // Receives
            if (requestCorrelation >= 0)
            {
                if (bytesTransferred == 0 || _input.ReaderCompleted.IsCompleted)
                {
                    _input.CompleteWriting();
                }
                else
                {
                    _buffer.CommitBytes((int)bytesTransferred);
                    _buffer.FlushAsync();

                    ProcessReceives();
                }
            }
            else
            {
                SendCompleting(requestCorrelation);
            }
        }
        /// <summary>
        /// Sends a command and it's parameters to the Stream.
        /// </summary>
        /// <param name="connection">The connection to the Redis Server.</param>
        /// <param name="command">The command.</param>
        /// <param name="parameters">The paramaters for the command.</param>
        public static async Task WriteCommandAsync(this RedisConnection connection,
                                                   string command,
                                                   IEnumerable <object> parameters = null)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }
            if (connection == null)
            {
                throw new ArgumentNullException(nameof(connection));
            }

            var            sizeOfCommandArray = 1 + (parameters?.Count() ?? 0);
            WritableBuffer output             = connection.Output.Alloc();

            // output the command array start
            output.Write(RedisProtocol.Utf8ArrayStart);
            output.Append(sizeOfCommandArray, TextEncoder.Utf8);
            output.Write(RedisProtocol.Utf8CRLF);

            // output the command
            var commandData = (Utf8String)command;

            WriteRedisBulkString(output, commandData);

            if (sizeOfCommandArray > 1)
            {
                foreach (object obj in parameters)
                {
                    WriteObject(output, obj);
                }
            }

            await output.FlushAsync();

            // TODO: should I call this?
            // connection.Output.Complete();
        }
예제 #9
0
        private async Task ReceiveFromSocketAndPushToWriterAsync()
        {
            SocketAsyncEventArgs args = null;

            try
            {
                // wait for someone to be interested in data before we
                // start allocating buffers and probing the socket
                args = GetOrCreateSocketAsyncEventArgs();
                while (!_stopping)
                {
                    bool           haveWriteBuffer = false;
                    WritableBuffer buffer          = default(WritableBuffer);
                    var            initialSegment  = default(ArraySegment <byte>);

                    try
                    {
                        int bytesFromInitialDataBuffer = 0;

                        if (Socket.Available == 0)
                        {
                            // now, this gets a bit messy unfortunately, because support for the ideal option
                            // (zero-length reads) is platform dependent
                            switch (_bufferStyle)
                            {
                            case BufferStyle.Unknown:
                                try
                                {
                                    initialSegment = await ReceiveInitialDataUnknownStrategyAsync(args);
                                }
                                catch
                                {
                                    initialSegment = default(ArraySegment <byte>);
                                }
                                if (initialSegment.Array == null)
                                {
                                    continue;     // redo from start
                                }
                                break;

                            case BufferStyle.UseZeroLengthBuffer:
                                // if we already have a buffer, use that (but: zero count); otherwise use a shared
                                // zero-length; this avoids constantly changing the buffer that the args use, which
                                // avoids some overheads
                                args.SetBuffer(args.Buffer ?? _zeroLengthBuffer, 0, 0);

                                // await async for the io work to be completed
                                await Socket.ReceiveSignalAsync(args);

                                break;

                            case BufferStyle.UseSmallBuffer:
                                // We need  to do a speculative receive with a *cheap* buffer while we wait for input; it would be *nice* if
                                // we could do a zero-length receive, but this is not supported equally on all platforms (fine on Windows, but
                                // linux hates it). The key aim here is to make sure that we don't tie up an entire block from the memory pool
                                // waiting for input on a socket; fine for 1 socket, not so fine for 100,000 sockets

                                // do a short receive while we wait (async) for data
                                initialSegment = LeaseSmallBuffer();
                                args.SetBuffer(initialSegment.Array, initialSegment.Offset, initialSegment.Count);

                                // await async for the io work to be completed
                                await Socket.ReceiveSignalAsync(args);

                                break;
                            }
                            if (args.SocketError != SocketError.Success)
                            {
                                throw new SocketException((int)args.SocketError);
                            }

                            // note we can't check BytesTransferred <= 0, as we always
                            // expect 0; but if we returned, we expect data to be
                            // buffered *on the socket*, else EOF
                            if ((bytesFromInitialDataBuffer = args.BytesTransferred) <= 0)
                            {
                                if (ReferenceEquals(initialSegment.Array, _zeroLengthBuffer))
                                {
                                    // sentinel value that means we should just
                                    // consume sync (we expect there to be data)
                                    initialSegment = default(ArraySegment <byte>);
                                }
                                else
                                {
                                    // socket reported EOF
                                    RecycleSmallBuffer(ref initialSegment);
                                }
                                if (Socket.Available == 0)
                                {
                                    // yup, definitely an EOF
                                    break;
                                }
                            }
                        }

                        // note that we will try to coalesce things here to reduce the number of flushes; we
                        // certainly want to coalesce the initial buffer (from the speculative receive) with the initial
                        // data, but we probably don't want to buffer indefinitely; for now, it will buffer up to 4 pages
                        // before flushing (entirely arbitrarily) - might want to make this configurable later
                        buffer          = _input.Writer.Alloc(SmallBufferSize * 2);
                        haveWriteBuffer = true;

                        const int FlushInputEveryBytes = 4 * MemoryPool.MaxPooledBlockLength;

                        if (initialSegment.Array != null)
                        {
                            // need to account for anything that we got in the speculative receive
                            if (bytesFromInitialDataBuffer != 0)
                            {
                                buffer.Write(new Span <byte>(initialSegment.Array, initialSegment.Offset, bytesFromInitialDataBuffer));
                            }
                            // make the small buffer available to other consumers
                            RecycleSmallBuffer(ref initialSegment);
                        }

                        bool isEOF = false;
                        while (Socket.Available != 0 && buffer.BytesWritten < FlushInputEveryBytes)
                        {
                            buffer.Ensure(); // ask for *something*, then use whatever is available (usually much much more)
                            SetBuffer(buffer.Buffer, args);
                            // await async for the io work to be completed
                            await Socket.ReceiveSignalAsync(args);

                            // either way, need to validate
                            if (args.SocketError != SocketError.Success)
                            {
                                throw new SocketException((int)args.SocketError);
                            }
                            int len = args.BytesTransferred;
                            if (len <= 0)
                            {
                                // socket reported EOF
                                isEOF = true;
                                break;
                            }

                            // record what data we filled into the buffer
                            buffer.Advance(len);
                        }
                        if (isEOF)
                        {
                            break;
                        }
                    }
                    finally
                    {
                        RecycleSmallBuffer(ref initialSegment);
                        if (haveWriteBuffer)
                        {
                            _stopping = (await buffer.FlushAsync()).IsCompleted;
                        }
                    }
                }
                _input.Writer.Complete();
            }
            catch (Exception ex)
            {
                // don't trust signal after an error; someone else could
                // still have it and invoke Set
                if (args != null)
                {
                    args.UserToken = null;
                }
                _input?.Writer.Complete(ex);
            }
            finally
            {
                try
                {
                    Socket.Shutdown(SocketShutdown.Receive);
                }
                catch { }

                RecycleSocketAsyncEventArgs(args);
            }
        }
예제 #10
0
        public async Task FlushAsync()
        {
            await _writableBuffer.FlushAsync();

            _needAlloc = true;
        }
예제 #11
0
 public WritableBufferAwaitable FlushAsync() => _innerBuffer.FlushAsync();
예제 #12
0
 public void ReceiveEndComplete()
 {
     _buffer.FlushAsync();
 }
예제 #13
0
 public async Task FlushAsync(WritableBuffer buffer)
 {
     await buffer.FlushAsync();
 }