private void RehandshakeCompleteCallback(IAsyncResult result)
{
LazyAsyncResult lazyAsyncResult = (LazyAsyncResult)result;
if (lazyAsyncResult == null)
{
NetEventSource.Fail(this, "result is null!");
}
if (!lazyAsyncResult.InternalPeekCompleted)
{
NetEventSource.Fail(this, "result is not completed!");
}
// If the rehandshake succeeded, FinishHandshake has already been called; if there was a SocketException
// during the handshake, this gets called directly from FixedSizeReader, and we need to call
// FinishHandshake to wake up the Read that triggered this rehandshake so the error gets back to the caller
Exception exception = lazyAsyncResult.InternalWaitForCompletion() as Exception;
if (exception != null)
{
// We may be calling FinishHandshake reentrantly, as FinishHandshake can call
// asyncRequest.CompleteWithError, which will result in this method being called.
// This is not a problem because:
//
// 1. We pass null as the asyncRequest parameter, so this second call to FinishHandshake won't loop
// back here.
//
// 2. _QueuedWriteStateRequest and _QueuedReadStateRequest are set to null after the first call,
// so, we won't invoke their callbacks again.
//
// 3. SetException won't overwrite an already-set _Exception.
//
// 4. There are three possibilities for _LockReadState and _LockWriteState:
//
// a. They were set back to None by the first call to FinishHandshake, and this will set them to
// None again: a no-op.
//
// b. They were set to None by the first call to FinishHandshake, but as soon as the lock was given
// up, another thread took a read/write lock. Calling FinishHandshake again will set them back
// to None, but that's fine because that thread will be throwing _Exception before it actually
// does any reading or writing and setting them back to None in a catch block anyways.
//
// c. If there is a Read/Write going on another thread, and the second FinishHandshake clears its
// read/write lock, it's fine because no other Read/Write can look at the lock until the current
// one gives up _SslStream._NestedRead/Write, and no handshake will look at the lock because
// handshakes are only triggered in response to successful reads (which won't happen once
// _Exception is set).
FinishHandshake(exception, null);
}
}