示例#1
0
        /// <summary>Rents a <see cref="Int32TaskSocketAsyncEventArgs"/> for immediate use.</summary>
        /// <param name="isReceive">true if this instance will be used for a receive; false if for sends.</param>
        private Int32TaskSocketAsyncEventArgs RentSocketAsyncEventArgs(bool isReceive)
        {
            // Get any cached SocketAsyncEventArg we may have.
            Int32TaskSocketAsyncEventArgs saea = isReceive ?
                                                 Interlocked.Exchange(ref _cachedReceiveEventArgs, s_rentedSentinel) :
                                                 Interlocked.Exchange(ref _cachedSendEventArgs, s_rentedSentinel);

            if (saea == s_rentedSentinel)
            {
                // An instance was once created (or is currently being created elsewhere), but some other
                // concurrent operation is using it. Since we can store at most one, and since an individual
                // APM operation is less expensive than creating a new SAEA and using it only once, we simply
                // return null, for a caller to fall back to using an APM implementation.
                return(null);
            }

            if (saea == null)
            {
                // No instance has been created yet, so create one.
                saea = new Int32TaskSocketAsyncEventArgs();
                var handler = isReceive ? // branch to avoid capturing isReceive on every call
                              new EventHandler <SocketAsyncEventArgs>((_, e) => CompleteSendReceive((Int32TaskSocketAsyncEventArgs)e, isReceive: true)) :
                              new EventHandler <SocketAsyncEventArgs>((_, e) => CompleteSendReceive((Int32TaskSocketAsyncEventArgs)e, isReceive: false));
                saea.Completed += handler;
            }

            // We got an instance. Configure and return it.
            saea.UserToken = this;
            return(saea);
        }
示例#2
0
        /// <summary>Returns a <see cref="Int32TaskSocketAsyncEventArgs"/> instance for reuse.</summary>
        /// <param name="saea">The instance to return.</param>
        /// <param name="isReceive">true if this instance is used for receives; false if used for sends.</param>
        private void ReturnSocketAsyncEventArgs(Int32TaskSocketAsyncEventArgs saea, bool isReceive)
        {
            Debug.Assert(saea != s_rentedSentinel);

            // Reset state on the SAEA before returning it.  But do not reset buffer state.  That'll be done
            // if necessary by the consumer, but we want to keep the buffers due to likely subsequent reuse
            // and the costs associated with changing them.
            saea.UserToken = null;
            saea._accessed = false;
            saea._builder  = default(AsyncTaskMethodBuilder <int>);
            saea._wrapExceptionsInIOExceptions = false;

            // Write this instance back as a cached instance.  It should only ever be overwriting the sentinel,
            // never null or another instance.
            if (isReceive)
            {
                Debug.Assert(_cachedReceiveEventArgs == s_rentedSentinel);
                Volatile.Write(ref _cachedReceiveEventArgs, saea);
            }
            else
            {
                Debug.Assert(_cachedSendEventArgs == s_rentedSentinel);
                Volatile.Write(ref _cachedSendEventArgs, saea);
            }
        }
示例#3
0
        /// <summary>Rents a <see cref="Int32TaskSocketAsyncEventArgs"/> for immediate use.</summary>
        /// <param name="isReceive">true if this instance will be used for a receive; false if for sends.</param>
        private Int32TaskSocketAsyncEventArgs RentSocketAsyncEventArgs(bool isReceive)
        {
            // Get any cached SocketAsyncEventArg we may have.
            CachedTaskEventArgs           cea  = LazyInitializer.EnsureInitialized(ref _cachedTaskEventArgs);
            Int32TaskSocketAsyncEventArgs saea = isReceive ?
                                                 Interlocked.Exchange(ref cea.Receive, s_rentedInt32Sentinel) :
                                                 Interlocked.Exchange(ref cea.Send, s_rentedInt32Sentinel);

            if (saea == s_rentedInt32Sentinel)
            {
                // An instance was once created (or is currently being created elsewhere), but some other
                // concurrent operation is using it. Since we can store at most one, and since an individual
                // APM operation is less expensive than creating a new SAEA and using it only once, we simply
                // return null, for a caller to fall back to using an APM implementation.
                return(null);
            }

            if (saea == null)
            {
                // No instance has been created yet, so create one.
                saea            = new Int32TaskSocketAsyncEventArgs();
                saea.Completed += isReceive ? ReceiveCompletedHandler : SendCompletedHandler;
            }

            return(saea);
        }
示例#4
0
        /// <summary>Completes the SocketAsyncEventArg's Task with the result of the send or receive, and returns it to the specified pool.</summary>
        private static void CompleteSendReceive(Int32TaskSocketAsyncEventArgs saea, bool isReceive)
        {
            // Pull the relevant state off of the SAEA
            Socket      s                            = (Socket)saea.UserToken;
            SocketError error                        = saea.SocketError;
            int         bytesTransferred             = saea.BytesTransferred;
            bool        wrapExceptionsInIOExceptions = saea._wrapExceptionsInIOExceptions;

            // Synchronize with the initiating thread. If the synchronous caller already got what
            // it needs from the SAEA, then we can return it to the pool now. Otherwise, it'll be
            // responsible for returning it once it's gotten what it needs from it.
            bool responsibleForReturningToPool;
            AsyncTaskMethodBuilder <int> builder = saea.GetCompletionResponsibility(out responsibleForReturningToPool);

            if (responsibleForReturningToPool)
            {
                s.ReturnSocketAsyncEventArgs(saea, isReceive);
            }

            // Complete the builder/task with the results.
            if (error == SocketError.Success)
            {
                builder.SetResult(bytesTransferred);
            }
            else
            {
                builder.SetException(GetException(error, wrapExceptionsInIOExceptions));
            }
        }
示例#5
0
        /// <summary>Gets a task to represent the operation.</summary>
        /// <param name="pending">true if the operation completes asynchronously; false if it completed synchronously.</param>
        /// <param name="saea">The event args instance used with the operation.</param>
        /// <param name="fromNetworkStream">
        /// true if the request is coming from NetworkStream, which has special semantics for
        /// exceptions and cached tasks; otherwise, false.
        /// </param>
        /// <param name="isReceive">true if this is a receive; false if this is a send.</param>
        private Task <int> GetTaskForSendReceive(
            bool pending, Int32TaskSocketAsyncEventArgs saea,
            bool fromNetworkStream, bool isReceive)
        {
            Task <int> t;

            if (pending)
            {
                // The operation is completing asynchronously (it may have already completed).
                // Get the task for the operation, with appropriate synchronization to coordinate
                // with the async callback that'll be completing the task.
                bool responsibleForReturningToPool;
                t = saea.GetCompletionResponsibility(out responsibleForReturningToPool).Task;
                if (responsibleForReturningToPool)
                {
                    // We're responsible for returning it only if the callback has already been invoked
                    // and gotten what it needs from the SAEA; otherwise, the callback will return it.
                    ReturnSocketAsyncEventArgs(saea, isReceive);
                }
            }
            else
            {
                // The operation completed synchronously.  Get a task for it.
                if (saea.SocketError == SocketError.Success)
                {
                    // Get the number of bytes successfully received/sent.
                    int bytesTransferred = saea.BytesTransferred;

                    // For zero bytes transferred, we can return our cached 0 task.
                    // We can also do so if the request came from network stream and is a send,
                    // as for that we can return any value because it returns a non-generic Task.
                    if (bytesTransferred == 0 || (fromNetworkStream & !isReceive))
                    {
                        t = s_zeroTask;
                    }
                    else
                    {
                        // Get any cached, successfully-completed cached task that may exist on this SAEA.
                        Task <int> lastTask = saea._successfullyCompletedTask;
                        Debug.Assert(lastTask == null || lastTask.Status == TaskStatus.RanToCompletion);

                        // If there is a task and if it has the desired result, simply reuse it.
                        // Otherwise, create a new one for this result value, and in addition to returning it,
                        // also store it into the SAEA for potential future reuse.
                        t = lastTask != null && lastTask.Result == bytesTransferred ?
                            lastTask :
                            (saea._successfullyCompletedTask = Task.FromResult(bytesTransferred));
                    }
                }
                else
                {
                    t = Task.FromException <int>(GetException(saea.SocketError, wrapExceptionsInIOExceptions: fromNetworkStream));
                }

                // There won't be a callback, and we're done with the SAEA, so return it to the pool.
                ReturnSocketAsyncEventArgs(saea, isReceive);
            }

            return(t);
        }
示例#6
0
        internal ValueTask <int> SendAsync(ReadOnlyMemory <byte> buffer, SocketFlags socketFlags, bool fromNetworkStream, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(new ValueTask <int>(Task.FromCanceled <int>(cancellationToken)));
            }

            // TODO https://github.com/dotnet/corefx/issues/24430:
            // Fully plumb cancellation down into socket operations.

            Int32TaskSocketAsyncEventArgs saea = RentSocketAsyncEventArgs(isReceive: false);

            if (saea != null)
            {
                // We got a cached instance. Configure the buffer and initate the operation.
                ConfigureBuffer(saea, MemoryMarshal.AsMemory <byte>(buffer), socketFlags, wrapExceptionsInIOExceptions: fromNetworkStream);
                return(GetValueTaskForSendReceive(SendAsync(saea), saea, fromNetworkStream, isReceive: false));
            }
            else
            {
                // We couldn't get a cached instance, due to a concurrent send operation on the socket.
                // Fall back to wrapping APM.
                return(new ValueTask <int>(SendAsyncApm(buffer, socketFlags)));
            }
        }
示例#7
0
        /// <summary>Gets a value task to represent the operation.</summary>
        /// <param name="pending">true if the operation completes asynchronously; false if it completed synchronously.</param>
        /// <param name="saea">The event args instance used with the operation.</param>
        /// <param name="fromNetworkStream">
        /// true if the request is coming from NetworkStream, which has special semantics for
        /// exceptions and cached tasks; otherwise, false.
        /// </param>
        /// <param name="isReceive">true if this is a receive; false if this is a send.</param>
        private ValueTask <int> GetValueTaskForSendReceive(
            bool pending, Int32TaskSocketAsyncEventArgs saea,
            bool fromNetworkStream, bool isReceive)
        {
            ValueTask <int> t;

            if (pending)
            {
                // The operation is completing asynchronously (it may have already completed).
                // Get the task for the operation, with appropriate synchronization to coordinate
                // with the async callback that'll be completing the task.
                bool responsibleForReturningToPool;
                t = new ValueTask <int>(saea.GetCompletionResponsibility(out responsibleForReturningToPool).Task);
                if (responsibleForReturningToPool)
                {
                    // We're responsible for returning it only if the callback has already been invoked
                    // and gotten what it needs from the SAEA; otherwise, the callback will return it.
                    ReturnSocketAsyncEventArgs(saea, isReceive);
                }
            }
            else
            {
                // The operation completed synchronously.  Return a ValueTask for it.
                t = saea.SocketError == SocketError.Success ?
                    new ValueTask <int>(saea.BytesTransferred) :
                    new ValueTask <int>(Task.FromException <int>(GetException(saea.SocketError, wrapExceptionsInIOExceptions: fromNetworkStream)));

                // There won't be a callback, and we're done with the SAEA, so return it to the pool.
                ReturnSocketAsyncEventArgs(saea, isReceive);
            }

            return(t);
        }
示例#8
0
 private static void ConfigureBufferList(
     Int32TaskSocketAsyncEventArgs saea, IList <ArraySegment <byte> > buffers, SocketFlags socketFlags)
 {
     // Configure the buffer list.  We don't clear the buffers when returning the SAEA to the pool,
     // so as to minimize overhead if the same buffers are used for subsequent operations (which is likely).
     // But SAEA doesn't support having both a buffer and a buffer list configured, so clear out a buffer
     // if there is one before we set the desired buffer list.
     if (!saea.MemoryBuffer.Equals(default))
示例#9
0
        internal Task <int> SendAsync(ArraySegment <byte> buffer, SocketFlags socketFlags, bool wrapExceptionsInIOExceptions)
        {
            // Validate the arguments.
            ValidateBuffer(buffer);

            // Get the SocketAsyncEventArgs instance to use for the operation.
            Int32TaskSocketAsyncEventArgs saea = RentSocketAsyncEventArgs(isReceive: false);

            if (saea == null)
            {
                // We couldn't get a cached instance, which means there's already a receive operation
                // happening on this socket.  Fall back to wrapping APM.
                var tcs = new TaskCompletionSource <int>(this);
                BeginSend(buffer.Array, buffer.Offset, buffer.Count, socketFlags, iar =>
                {
                    var innerTcs = (TaskCompletionSource <int>)iar.AsyncState;
                    try { innerTcs.TrySetResult(((Socket)innerTcs.Task.AsyncState).EndSend(iar)); }
                    catch (Exception e) { innerTcs.TrySetException(e); }
                }, tcs);
                return(tcs.Task);
            }

            // Configure the buffer.  We don't clear the buffers when returning the SAEA to the pool,
            // so as to minimize overhead if the same buffer is used for subsequent operations (which is likely).
            // But SAEA doesn't support having both a buffer and a buffer list configured, so clear out a buffer list
            // if there is one before we set the desired buffer.
            if (saea.BufferList != null)
            {
                saea.BufferList = null;
            }
            saea.SetBuffer(buffer.Array, buffer.Offset, buffer.Count);
            saea.SocketFlags = socketFlags;
            saea.WrapExceptionsInIOExceptions = wrapExceptionsInIOExceptions;

            // Initiate the send
            Task <int> t;

            if (!SendAsync(saea))
            {
                // The operation completed synchronously.  Get a task for it and return the SAEA for future use.
                t = saea.SocketError == SocketError.Success ?
                    GetSuccessTask(saea) :
                    Task.FromException <int>(GetException(saea.SocketError, wrapExceptionsInIOExceptions));
                ReturnSocketAsyncEventArgs(saea, isReceive: false);
            }
            else
            {
                // The operation completed asynchronously.  Get the task for the operation,
                // with appropriate synchronization to coordinate with the async callback
                // that'll be completing the task.
                t = saea.GetTaskSafe();
            }
            return(t);
        }
示例#10
0
        /// <summary>Dispose of any cached <see cref="Int32TaskSocketAsyncEventArgs"/> instances.</summary>
        private void DisposeCachedTaskSocketAsyncEventArgs()
        {
            Int32TaskSocketAsyncEventArgs e = Interlocked.Exchange(ref _cachedReceiveEventArgs, s_rentedSentinel);

            if (e != s_rentedSentinel)
            {
                e?.Dispose();
            }

            e = Interlocked.Exchange(ref _cachedSendEventArgs, s_rentedSentinel);
            if (e != s_rentedSentinel)
            {
                e?.Dispose();
            }
        }
示例#11
0
 private static void ConfigureBuffer(
     Int32TaskSocketAsyncEventArgs saea, ArraySegment <byte> buffer, SocketFlags socketFlags, bool wrapExceptionsInIOExceptions)
 {
     // Configure the buffer.  We don't clear the buffers when returning the SAEA to the pool,
     // so as to minimize overhead if the same buffer is used for subsequent operations (which is likely).
     // But SAEA doesn't support having both a buffer and a buffer list configured, so clear out a buffer list
     // if there is one before we set the desired buffer.
     if (saea.BufferList != null)
     {
         saea.BufferList = null;
     }
     saea.SetBuffer(buffer.Array, buffer.Offset, buffer.Count);
     saea.SocketFlags = socketFlags;
     saea._wrapExceptionsInIOExceptions = wrapExceptionsInIOExceptions;
 }
示例#12
0
        /// <summary>Gets a <see cref="Task{Int32}"/> that represents the BytesTransferred from a successful send/receive.</summary>
        private static Task <int> GetSuccessTask(Int32TaskSocketAsyncEventArgs saea)
        {
            // Get the number of bytes successfully received/sent.
            int bytesTransferred = saea.BytesTransferred;

            // And get any cached, successfully-completed cached task that may exist on this SAEA.
            Task <int> lastTask = saea.SuccessfullyCompletedTask;

            Debug.Assert(lastTask == null || lastTask.Status == TaskStatus.RanToCompletion);

            // If there is a task and if it has the desired result, simply reuse it.
            // Otherwise, create a new one for this result value, and in addition to returning it,
            // also store it into the SAEA for potential future reuse.
            return(lastTask != null && lastTask.Result == bytesTransferred ?
                   lastTask :
                   (saea.SuccessfullyCompletedTask = Task.FromResult(bytesTransferred)));
        }
示例#13
0
        internal Task <int> SendAsync(IList <ArraySegment <byte> > buffers, SocketFlags socketFlags)
        {
            // Validate the arguments.
            ValidateBuffersList(buffers);

            Int32TaskSocketAsyncEventArgs saea = RentSocketAsyncEventArgs(isReceive: false);

            if (saea != null)
            {
                // We got a cached instance. Configure the buffer list and initate the operation.
                ConfigureBufferList(saea, buffers, socketFlags);
                return(GetTaskForSendReceive(SendAsync(saea), saea, wrapExceptionsInIOExceptions: false, isReceive: false));
            }
            else
            {
                // We couldn't get a cached instance, due to a concurrent send operation on the socket.
                // Fall back to wrapping APM.
                return(SendAsyncApm(buffers, socketFlags));
            }
        }
示例#14
0
        internal Task <int> ReceiveAsync(ArraySegment <byte> buffer, SocketFlags socketFlags, bool wrapExceptionsInIOExceptions)
        {
            // Validate the arguments.
            ValidateBuffer(buffer);

            Int32TaskSocketAsyncEventArgs saea = RentSocketAsyncEventArgs(isReceive: true);

            if (saea != null)
            {
                // We got a cached instance. Configure the buffer and initate the operation.
                ConfigureBuffer(saea, buffer, socketFlags, wrapExceptionsInIOExceptions);
                return(GetTaskForSendReceive(ReceiveAsync(saea), saea, wrapExceptionsInIOExceptions, isReceive: true));
            }
            else
            {
                // We couldn't get a cached instance, due to a concurrent receive operation on the socket.
                // Fall back to wrapping APM.
                return(ReceiveAsyncApm(buffer, socketFlags));
            }
        }
示例#15
0
        /// <summary>Completes the SocketAsyncEventArg's Task with the result of the send or receive, and returns it to the specified pool.</summary>
        private static void CompleteSendReceive(Int32TaskSocketAsyncEventArgs saea, bool isReceive)
        {
            // Synchronize with the initiating thread accessing the task from the builder.
            saea.GetTaskSafe();

            // Pull the relevant state off of the SAEA and only then return it to the pool.
            Socket s = (Socket)saea.UserToken;
            AsyncTaskMethodBuilder <int> builder = saea.Builder;
            SocketError error                        = saea.SocketError;
            int         bytesTransferred             = saea.BytesTransferred;
            bool        wrapExceptionsInIOExceptions = saea.WrapExceptionsInIOExceptions;

            s.ReturnSocketAsyncEventArgs(saea, isReceive);

            // Complete the builder/task with the results.
            if (error == SocketError.Success)
            {
                builder.SetResult(bytesTransferred);
            }
            else
            {
                builder.SetException(GetException(error, wrapExceptionsInIOExceptions));
            }
        }