private Task SendFrameAsync(MessageOpcode opcode, bool endOfMessage, ArraySegment<byte> payloadBuffer, CancellationToken cancellationToken)
{
// TODO: #4900 SendFrameAsync should in theory typically complete synchronously, making it fast and allocation free.
// However, due to #4900, it almost always yields, resulting in all of the allocations involved in an async method
// yielding, e.g. the boxed state machine, the Action delegate, the MoveNextRunner, and the resulting Task, plus it's
// common that the awaited operation completes so fast after the await that we may end up allocating an AwaitTaskContinuation
// inside of the TaskAwaiter. Since SendFrameAsync is such a core code path, until that can be fixed, we put some
// optimizations in place to avoid a few of those expenses, at the expense of more complicated code; for the common case,
// this code has fewer than half the number and size of allocations. If/when that issue is fixed, this method should be deleted
// and replaced by SendFrameFallbackAsync, which is the same logic but in a much more easily understand flow.
// If a cancelable cancellation token was provided, that would require registering with it, which means more state we have to
// pass around (the CancellationTokenRegistration), so if it is cancelable, just immediately go to the fallback path.
// Similarly, it should be rare that there are multiple outstanding calls to SendFrameAsync, but if there are, again
// fall back to the fallback path.
return cancellationToken.CanBeCanceled || !_sendFrameAsyncLock.Wait(0) ?
SendFrameFallbackAsync(opcode, endOfMessage, payloadBuffer, cancellationToken) :
SendFrameLockAcquiredNonCancelableAsync(opcode, endOfMessage, payloadBuffer);
}