|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements.
|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 |
|
| 4 | +using System.Buffers; |
4 | 5 | using System.Diagnostics;
|
5 | 6 | using System.Net.Quic.Implementations.MsQuic.Internal;
|
6 | 7 | using System.Net.Security;
|
@@ -133,7 +134,7 @@ public void SetClosing()
|
133 | 134 | _closing = true;
|
134 | 135 | }
|
135 | 136 | }
|
136 |
| - |
| 137 | + |
137 | 138 | public CancellationTokenSource Abort = new();
|
138 | 139 |
|
139 | 140 | public event QuicDatagramReceivedEventHandler? DatagramReceived;
|
@@ -526,22 +527,9 @@ private static uint HandleEventDatagramSendStateChanged(State state, ref Connect
|
526 | 527 | {
|
527 | 528 | var datagramState = connectionEvent.Data.DatagramSendStateChanged.State;
|
528 | 529 | GCHandle handle = GCHandle.FromIntPtr(connectionEvent.Data.DatagramSendStateChanged.ClientContext);
|
529 |
| - if (handle.Target is TaskCompletionSource<QUIC_DATAGRAM_SEND_STATE> source) |
530 |
| - { |
531 |
| - switch (datagramState) |
532 |
| - { |
533 |
| - case QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_LOST_DISCARDED: |
534 |
| - case QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_ACKNOWLEDGED: |
535 |
| - case QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_ACKNOWLEDGED_SPURIOUS: |
536 |
| - case QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_CANCELED: |
537 |
| - source.TrySetResult(datagramState); |
538 |
| - break; |
539 |
| - default: |
540 |
| - break; |
541 |
| - } |
542 |
| - return MsQuicStatusCodes.Success; |
543 |
| - } |
544 |
| - return MsQuicStatusCodes.InvalidState; |
| 530 | + return handle.Target is Func<QUIC_DATAGRAM_SEND_STATE, bool> callback && callback(datagramState) ? |
| 531 | + MsQuicStatusCodes.Success : |
| 532 | + MsQuicStatusCodes.InvalidState; |
545 | 533 | }
|
546 | 534 |
|
547 | 535 | internal override async ValueTask<QuicStreamProvider> AcceptStreamAsync(CancellationToken cancellationToken = default)
|
@@ -967,43 +955,93 @@ internal override event QuicDatagramReceivedEventHandler? DatagramReceived
|
967 | 955 | remove => _state.DatagramReceived -= value;
|
968 | 956 | }
|
969 | 957 |
|
970 |
| - internal override async ValueTask<bool> SendDatagramAsync(ReadOnlyMemory<byte> buffer, bool priority) |
| 958 | + internal override Task<QuicDatagramSendingResult> SendDatagramAsync(ReadOnlyMemory<byte> buffer, bool priority) |
971 | 959 | {
|
972 |
| - TaskCompletionSource<QUIC_DATAGRAM_SEND_STATE> tcs = new(); |
973 |
| - var tcsHandle = GCHandle.Alloc(tcs); |
974 |
| - using var handle = buffer.Pin(); |
975 |
| - var quicBuffer = new QuicBuffer[1]; |
976 |
| - quicBuffer[0].Length = (uint)buffer.Length; |
977 |
| - unsafe { quicBuffer[0].Buffer = (byte*)handle.Pointer; } |
978 |
| - var quicBufferHandle = GCHandle.Alloc(quicBuffer, GCHandleType.Pinned); |
| 960 | + return SendDatagramAsync(new ReadOnlySequence<byte>(buffer), priority); |
| 961 | + } |
| 962 | + |
| 963 | + internal unsafe override Task<QuicDatagramSendingResult> SendDatagramAsync(ReadOnlySequence<byte> buffers, bool priority) |
| 964 | + { |
| 965 | + TaskCompletionSource<QuicDatagramSendingResult> sent = new(); |
| 966 | + TaskCompletionSource completion = new(), lostSuspect = new(); |
| 967 | + Func<QUIC_DATAGRAM_SEND_STATE, bool> callback = state => state switch |
| 968 | + { |
| 969 | + QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SENT => |
| 970 | + sent.TrySetResult(new(completion.Task, lostSuspect.Task)), |
| 971 | + QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_LOST_SUSPECT => |
| 972 | + lostSuspect.TrySetException(new TimeoutException()), |
| 973 | + QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_LOST_DISCARDED => |
| 974 | + completion.TrySetException(new TimeoutException()), |
| 975 | + QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_ACKNOWLEDGED or |
| 976 | + QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_ACKNOWLEDGED_SPURIOUS => |
| 977 | + lostSuspect.TrySetResult() | completion.TrySetResult(), |
| 978 | + QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_CANCELED => |
| 979 | + sent.TrySetCanceled() | lostSuspect.TrySetCanceled() | completion.TrySetCanceled(), |
| 980 | + _ => false |
| 981 | + }; |
| 982 | + var count = 0; |
| 983 | + foreach (var buffer in buffers) |
| 984 | + { |
| 985 | + ++count; |
| 986 | + } |
| 987 | + var handles = new MemoryHandle[count]; |
| 988 | + nint quicBuffers; |
| 989 | + var callbackHandle = GCHandle.Alloc(callback); |
979 | 990 | try
|
980 | 991 | {
|
981 |
| - unsafe |
| 992 | + quicBuffers = Marshal.AllocHGlobal(sizeof(QuicBuffer) * count); |
| 993 | + try |
982 | 994 | {
|
983 |
| - var status = MsQuicApi.Api.DatagramSendDelegate( |
984 |
| - _state.Handle, |
985 |
| - (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(quicBuffer, 0), |
986 |
| - 1, |
987 |
| - priority ? QUIC_SEND_FLAGS.DGRAM_PRIORITY : QUIC_SEND_FLAGS.NONE, |
988 |
| - (IntPtr)tcsHandle); |
989 |
| - QuicExceptionHelpers.ThrowIfFailed(status, "Failed to send a datagram to peer."); |
| 995 | + completion.Task.ContinueWith(task => |
| 996 | + { |
| 997 | + foreach (var handle in handles) |
| 998 | + { |
| 999 | + using (handle) { } |
| 1000 | + } |
| 1001 | + Marshal.FreeHGlobal(quicBuffers); |
| 1002 | + callbackHandle.Free(); |
| 1003 | + }); |
| 1004 | + try |
| 1005 | + { |
| 1006 | + var pointer = (QuicBuffer*)quicBuffers; |
| 1007 | + count = 0; |
| 1008 | + foreach (var buffer in buffers) |
| 1009 | + { |
| 1010 | + var handle = buffer.Pin(); |
| 1011 | + pointer[count].Length = (uint)buffer.Length; |
| 1012 | + pointer[count].Buffer = (byte*)handle.Pointer; |
| 1013 | + handles[count] = handle; |
| 1014 | + ++count; |
| 1015 | + } |
| 1016 | + var status = MsQuicApi.Api.DatagramSendDelegate( |
| 1017 | + _state.Handle, |
| 1018 | + pointer, |
| 1019 | + (uint)count, |
| 1020 | + priority ? QUIC_SEND_FLAGS.DGRAM_PRIORITY : QUIC_SEND_FLAGS.NONE, |
| 1021 | + (IntPtr)callbackHandle); |
| 1022 | + QuicExceptionHelpers.ThrowIfFailed(status, "Failed to send a datagram to peer."); |
| 1023 | + } |
| 1024 | + catch |
| 1025 | + { |
| 1026 | + foreach (var handle in handles) |
| 1027 | + { |
| 1028 | + using (handle) { } |
| 1029 | + } |
| 1030 | + throw; |
| 1031 | + } |
990 | 1032 | }
|
991 |
| - using var abort = _state.Abort.Token.Register(() => tcs.SetException(ThrowHelper.GetConnectionAbortedException(_state.AbortErrorCode))); |
992 |
| - var state = await tcs.Task.ConfigureAwait(false); |
993 |
| - return state switch |
| 1033 | + catch |
994 | 1034 | {
|
995 |
| - QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_ACKNOWLEDGED => true, |
996 |
| - QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_ACKNOWLEDGED_SPURIOUS => false, |
997 |
| - QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_CANCELED => throw new OperationCanceledException("Datagram send canceled."), |
998 |
| - QUIC_DATAGRAM_SEND_STATE.QUIC_DATAGRAM_SEND_LOST_DISCARDED => throw new QuicDatagramException("Datagram lost discarded.", (int)state), |
999 |
| - _ => throw new QuicDatagramException("Unknown datagram send state.", (int)state) |
1000 |
| - }; |
| 1035 | + Marshal.FreeHGlobal(quicBuffers); |
| 1036 | + throw; |
| 1037 | + } |
1001 | 1038 | }
|
1002 |
| - finally |
| 1039 | + catch (Exception ex) |
1003 | 1040 | {
|
1004 |
| - quicBufferHandle.Free(); |
1005 |
| - tcsHandle.Free(); |
| 1041 | + callbackHandle.Free(); |
| 1042 | + sent.SetException(ex); |
1006 | 1043 | }
|
| 1044 | + return sent.Task; |
1007 | 1045 | }
|
1008 | 1046 |
|
1009 | 1047 | private void ThrowIfDisposed()
|
|
0 commit comments