Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

notes #55666

Closed
wants to merge 1 commit into from
Closed

notes #55666

Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ namespace System.Net.Quic.Implementations.MsQuic
internal sealed class MsQuicStream : QuicStreamProvider
{
// Delegate that wraps the static function that will be called when receiving an event.
// CR: callback calls for a single instance of a stream are serialized by msquic, happpen on a msquic thread and shouldn't take too long to not to block msquic.
internal static readonly StreamCallbackDelegate s_streamDelegate = new StreamCallbackDelegate(NativeCallbackHandler);

// CR: passed to msquic and returned in msquic callback.
private readonly State _state = new State();

private readonly bool _canRead;
Expand All @@ -30,7 +32,11 @@ internal sealed class MsQuicStream : QuicStreamProvider

private sealed class State
{
// CR: We could laverage DangerousAddRef to handle relation between connection and stream and properly order calls to close.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// CR: SHUTDOWN event on connection will not happen while there are still any streams associated, we could laverage this instead of SafeHandle ref counting.
public SafeMsQuicStreamHandle Handle = null!; // set in ctor.
// CR: roots the state in GC and it won't get collected while this exist.
// CR: must be kept alive until we receive SHUTDOWN_COMPLETE event
public GCHandle StateGCHandle;

public MsQuicStream? Stream; // roots the stream in the pinned state to prevent GC during an async read I/O.
Expand All @@ -50,6 +56,7 @@ private sealed class State
// set when ReadState.PendingRead:
public Memory<byte> ReceiveUserBuffer;
public CancellationTokenRegistration ReceiveCancellationRegistration;
// CR: Potentially we could get rid of back reference to the parent object since we don't care about keeping them alive while async msquic call is happening.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Resettable completions to be used for multiple calls to receive.
public readonly ResettableCompletionSource<int> ReceiveResettableCompletionSource = new ResettableCompletionSource<int>();

Expand All @@ -65,12 +72,16 @@ private sealed class State
// Resettable completions to be used for multiple calls to send.
public readonly ResettableCompletionSource<uint> SendResettableCompletionSource = new ResettableCompletionSource<uint>();

// CR: We should get rid off it, we're not interested in it.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public ShutdownWriteState ShutdownWriteState;

// Set once writes have been shutdown.
// CR: We should get rid of this
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public readonly TaskCompletionSource ShutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

// CR: Shutdown should be merged into CloseAsync.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public ShutdownState ShutdownState;
// CR: To make sure that we release the handles only once.
public int ShutdownDone;

// Set once stream have been shutdown.
Expand Down Expand Up @@ -113,16 +124,21 @@ internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHa
}
catch
{
// CR: we should release the handle here as well as we do in outbound. Does int interfere with this being run on msquic thread? Ask NICK.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Should we return a specific error code in MsQuicConnection to clean the handle for us?
_state.StateGCHandle.Free();
throw;
}

// CR: We should move this before callback handler registration.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!connectionState.TryAddStream(this))
{
_state.StateGCHandle.Free();
throw new ObjectDisposedException(nameof(QuicConnection));
}

// CR: We should set it before SetCallbackHandlerDelegate since we can get CONNETCION_CLOSED event and that needs this.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If we want to prevent decrementing stream count, we should explicitely unset here.
_state.ConnectionState = connectionState;

_state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle);
Expand Down Expand Up @@ -170,6 +186,7 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F
throw;
}

// CR: The same as with the inbound.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!connectionState.TryAddStream(this))
{
_state.Handle?.Dispose();
Expand All @@ -191,6 +208,7 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F

internal override bool CanRead => _disposed == 0 && _canRead;

// CR: Might be useful to introduce Is(Uni|Bi)Directional to distinguish the type of the stream instead of using Can*.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal override bool CanWrite => _disposed == 0 && _canWrite;

internal override long StreamId
Expand Down Expand Up @@ -220,6 +238,7 @@ internal override ValueTask WriteAsync(ReadOnlySequence<byte> buffers, Cancellat

internal override async ValueTask WriteAsync(ReadOnlySequence<byte> buffers, bool endStream, CancellationToken cancellationToken = default)
{
// CR: we should propagate exceptions up here for write state.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wfurt I don't exactly remember what the problem was, could you remind me before I create an issue?

ThrowIfDisposed();

using CancellationTokenRegistration registration = HandleWriteStartState(cancellationToken);
Expand Down Expand Up @@ -258,6 +277,8 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, bool e

private CancellationTokenRegistration HandleWriteStartState(CancellationToken cancellationToken)
{
// CR: we should make the flow as we do in Read, where we check the state and make transition in a lock as a first thing
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// and throw exceptions afterwards.
if (_state.SendState == SendState.Closed)
{
throw new InvalidOperationException(SR.net_quic_writing_notallowed);
Expand Down Expand Up @@ -286,6 +307,8 @@ private CancellationTokenRegistration HandleWriteStartState(CancellationToken ca
}

// if token was already cancelled, this would execute synchronously
// CR: we need to track the registration and dispose it even in case of exception thrown outside of this method.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// https://github.com/dotnet/runtime/issues/55437
CancellationTokenRegistration registration = cancellationToken.UnsafeRegister(static (s, token) =>
{
var state = (State)s!;
Expand Down Expand Up @@ -318,6 +341,7 @@ private CancellationTokenRegistration HandleWriteStartState(CancellationToken ca
throw new QuicStreamAbortedException(_state.SendErrorCode);
}

// CR: this should be some other exception, we should throw OCE unless CT has been fired.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new OperationCanceledException(SR.net_quic_sending_aborted);
}
else if (_state.SendState == SendState.ConnectionClosed)
Expand Down Expand Up @@ -555,6 +579,7 @@ private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode)
QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed.");
}

// CR: We should get rid of this
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal override async ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default)
{
ThrowIfDisposed();
Expand Down Expand Up @@ -1146,6 +1171,8 @@ private static void CleanupSendState(State state)
}

// TODO prevent overlapping sends or consider supporting it.
// CR: Why do we have 3 version of sending? We should unite as much as possible.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// CR: are we even testing all of them?
private unsafe ValueTask SendReadOnlyMemoryAsync(
ReadOnlyMemory<byte> buffer,
QUIC_SEND_FLAGS flags)
Expand Down Expand Up @@ -1200,6 +1227,7 @@ private unsafe ValueTask SendReadOnlyMemoryAsync(
return _state.SendResettableCompletionSource.GetTypelessValueTask();
}

// CR: for kestrel
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private unsafe ValueTask SendReadOnlySequenceAsync(
ReadOnlySequence<byte> buffers,
QUIC_SEND_FLAGS flags)
Expand Down