void IAsyncExecutionResultHandler.HandleAsyncResult(AsyncOperationResult aResult) { AsyncOperation?op = _executingOperation !; AsyncExecutionResult result = op.HandleAsyncResult(aResult); if (result != AsyncExecutionResult.Finished && op.IsCancellationRequested) { result = AsyncExecutionResult.Cancelled; } if (result == AsyncExecutionResult.Executing || result == AsyncExecutionResult.WaitForPoll) { result = op.TryExecuteIOUringAsync(_thread.ExecutionQueue, this, _keyForOperation); Debug.Assert(result == AsyncExecutionResult.Executing); return; } _executingOperation = null; AsyncOperation?next = CompleteOperationAndGetNext(op, result); if (next != null) { ExecuteQueued(next); } }
private AsyncOperation?CompleteOperationAndGetNext(AsyncOperation op, AsyncExecutionResult result) { if (result == AsyncExecutionResult.Finished) { op.Status = OperationStatus.Completed; } else // AsyncExecutionResult.WaitForPoll or Cancelled { if (result == AsyncExecutionResult.WaitForPoll) { OperationStatus previous = op.CompareExchangeStatus(OperationStatus.Queued, OperationStatus.Executing); if (previous == OperationStatus.Executing) { // We've changed from executing to queued. return(null); } Debug.Assert((previous & OperationStatus.CancellationRequested) != 0); } op.Status = (op.Status & ~(OperationStatus.CancellationRequested | OperationStatus.Executing)) | OperationStatus.Cancelled; } AsyncOperation?next = DequeueFirstAndGetNext(op); op.Complete(); return(next); }
void IAsyncExecutionResultHandler.HandleAsyncResult(AsyncOperationResult aResult) { AsyncOperation?op = _executingOperation !; AsyncExecutionResult result = op.HandleAsyncResult(aResult); if (result != AsyncExecutionResult.Finished && op.IsCancellationRequested) { result = AsyncExecutionResult.Cancelled; } if (result == AsyncExecutionResult.Executing) { result = op.TryExecuteEpollAsync(triggeredByPoll: false, _thread.ExecutionQueue, this); if (result == AsyncExecutionResult.Executing) { return; } } _executingOperation = null; AsyncOperation?next = CompleteOperationAndGetNext(op, result); if (next != null) { ExecuteQueued(triggeredByPoll: false, next); } }
public void ExecuteQueued(bool triggeredByPoll, AsyncOperation?op = null) { if (triggeredByPoll) { Interlocked.Increment(ref _eventCounter); } op ??= QueueGetFirst(); do { var spin = new SpinWait(); while (true) { if (op is null) { return; } OperationStatus previous = op.CompareExchangeStatus(OperationStatus.Executing, OperationStatus.Queued); if (previous == OperationStatus.Queued) { // We've changed from queued to executing. break; } else if ((previous & OperationStatus.Executing) != 0) // Also set when CancellationRequested. { // Already executing. return; } // Operation was cancelled, but not yet removed from queue. Debug.Assert((previous & OperationStatus.Cancelled) != 0); spin.SpinOnce(); op = QueueGetFirst(); } AsyncExecutionResult result = op.TryExecuteEpollAsync(triggeredByPoll, _thread.ExecutionQueue, this); if (result == AsyncExecutionResult.Executing) { _executingOperation = op; return; } op = CompleteOperationAndGetNext(op, result); } while (op != null); }
private unsafe AsyncExecutionResult SendMultipleBuffers(IList <ArraySegment <byte> > buffers, bool isCancellationRequested, bool asyncOnly, AsyncExecutionQueue?executionQueue, AsyncExecutionCallback?callback, object?state, int data, AsyncOperationResult asyncResult) { // TODO: really support multi-buffer sends... for (; _bufferIndex < buffers.Count; _bufferIndex++) { AsyncExecutionResult bufferSendResult = SendSingleBuffer(buffers[_bufferIndex], isCancellationRequested, asyncOnly, executionQueue, callback, state, data, asyncResult); if (bufferSendResult == AsyncExecutionResult.WaitForPoll || bufferSendResult == AsyncExecutionResult.Executing) { return(bufferSendResult); } if (SocketError != SocketError.Success) { break; } BytesTransferred = 0; // TODO... not really correct } return(AsyncExecutionResult.Finished); }
private AsyncOperation?CompleteOperationAndGetNext(AsyncOperation op, AsyncExecutionResult result) { if (result == AsyncExecutionResult.Finished) { op.Status = OperationStatus.Completed; } else // Cancelled { op.Status = (op.Status & ~(OperationStatus.CancellationRequested | OperationStatus.Executing)) | OperationStatus.Cancelled; } Volatile.Write(ref _cancellingOperation, null); AsyncOperation?next = DequeueFirstAndGetNext(op); op.Complete(); return(next); }
private AsyncExecutionResult SendSingleBuffer(Memory <byte> memory, bool isCancellationRequested, bool asyncOnly, AsyncExecutionQueue?executionQueue, AsyncExecutionCallback?callback, object?state, int data, AsyncOperationResult asyncResult) { SocketError socketError = SocketError.SocketError; AsyncExecutionResult result = AsyncExecutionResult.Executing; if (asyncResult.HasResult) { if (asyncResult.IsError) { if (asyncResult.Errno == EINTR) { result = AsyncExecutionResult.Executing; } else if (asyncResult.Errno == EAGAIN) { result = AsyncExecutionResult.WaitForPoll; } else { socketError = SocketPal.GetSocketErrorForErrno(asyncResult.Errno); result = AsyncExecutionResult.Finished; } } else { BytesTransferred += asyncResult.IntValue; if (BytesTransferred == memory.Length) { socketError = SocketError.Success; result = AsyncExecutionResult.Finished; } } } if (isCancellationRequested && result != AsyncExecutionResult.Finished) { SocketError = SocketError.OperationAborted; return(AsyncExecutionResult.Cancelled); } // When there is a pollable executionQueue, use it to poll, and then try the operation. if (result == AsyncExecutionResult.Executing || (result == AsyncExecutionResult.WaitForPoll && executionQueue?.SupportsPolling == true)) { Socket socket = Socket !; if (socket == null) { ThrowHelper.ThrowInvalidOperationException(); } if (executionQueue != null) { Memory <byte> remaining = memory.Slice(BytesTransferred); executionQueue.AddWrite(socket.SafeHandle, remaining, callback !, state, data); result = AsyncExecutionResult.Executing; } else if (result == AsyncExecutionResult.Executing) { if (asyncOnly) { result = AsyncExecutionResult.WaitForPoll; } else { while (true) { Memory <byte> remaining = memory.Slice(BytesTransferred); int bytesTransferred; (socketError, bytesTransferred) = SocketPal.Send(socket.SafeHandle, remaining); if (socketError == SocketError.Success) { BytesTransferred += bytesTransferred; if (BytesTransferred == memory.Length || bytesTransferred == 0) { break; } } else { break; } } result = socketError == SocketError.WouldBlock ? AsyncExecutionResult.WaitForPoll : AsyncExecutionResult.Finished; } } } if (result == AsyncExecutionResult.Finished) { SocketError = socketError; } return(result); }
public override AsyncExecutionResult TryExecute(bool triggeredByPoll, bool isCancellationRequested, bool asyncOnly, AsyncExecutionQueue?executionQueue, AsyncExecutionCallback?callback, object?state, int data, AsyncOperationResult asyncResult) { SocketError socketError = SocketError.SocketError; int bytesTransferred = -1; AsyncExecutionResult result = AsyncExecutionResult.Executing; if (asyncResult.HasResult) { if (asyncResult.IsError) { if (asyncResult.Errno == EINTR) { result = AsyncExecutionResult.Executing; } else if (asyncResult.Errno == EAGAIN) { result = AsyncExecutionResult.WaitForPoll; } else { bytesTransferred = 0; socketError = SocketPal.GetSocketErrorForErrno(asyncResult.Errno); result = AsyncExecutionResult.Finished; } } else { bytesTransferred = asyncResult.IntValue; socketError = SocketError.Success; result = AsyncExecutionResult.Finished; } } if (isCancellationRequested && result != AsyncExecutionResult.Finished) { SocketError = SocketError.OperationAborted; return(AsyncExecutionResult.Cancelled); } // When there is a pollable executionQueue, use it to poll, and then try the operation. if (result == AsyncExecutionResult.Executing || (result == AsyncExecutionResult.WaitForPoll && executionQueue?.SupportsPolling == true)) { Memory <byte> memory = MemoryBuffer; bool isPollingReadable = memory.Length == 0; // A zero-byte read is a poll. if (triggeredByPoll && isPollingReadable) { // No need to make a syscall, poll told us we're readable. (socketError, bytesTransferred) = (SocketError.Success, 0); result = AsyncExecutionResult.Finished; } else { Socket socket = Socket !; // Using Linux AIO executionQueue, we can't check when there is no // data available. Instead of return value EAGAIN, a 0-byte read returns '0'. if (executionQueue != null && (!isPollingReadable || executionQueue.SupportsPolling)) // Don't use Linux AIO for 0-byte reads. { executionQueue.AddRead(socket.SafeHandle, memory, callback !, state, data); result = AsyncExecutionResult.Executing; } else if (result == AsyncExecutionResult.Executing) { if (asyncOnly) { result = AsyncExecutionResult.WaitForPoll; } else { (socketError, bytesTransferred) = SocketPal.Recv(socket.SafeHandle, memory); result = socketError == SocketError.WouldBlock ? AsyncExecutionResult.WaitForPoll : AsyncExecutionResult.Finished; } } } } if (result == AsyncExecutionResult.Finished) { Debug.Assert(bytesTransferred != -1); BytesTransferred = bytesTransferred; SocketError = socketError; } return(result); }