2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
- using System . Diagnostics . CodeAnalysis ;
6
5
using System . Threading ;
7
6
using System . Threading . Tasks ;
7
+ using System . Threading . Tasks . Sources ;
8
8
using Microsoft . AspNetCore . Http ;
9
9
using Microsoft . AspNetCore . HttpSys . Internal ;
10
10
11
11
namespace Microsoft . AspNetCore . Server . HttpSys
12
12
{
13
- internal unsafe class AsyncAcceptContext : TaskCompletionSource < RequestContext > , IDisposable
13
+ internal unsafe class AsyncAcceptContext : IValueTaskSource < RequestContext > , IDisposable
14
14
{
15
- internal static readonly IOCompletionCallback IOCallback = new IOCompletionCallback ( IOWaitCallback ) ;
15
+ private static readonly IOCompletionCallback IOCallback = IOWaitCallback ;
16
+ private readonly PreAllocatedOverlapped _preallocatedOverlapped ;
17
+ private NativeOverlapped * _overlapped ;
18
+
19
+ // mutable struct; do not make this readonly
20
+ private ManualResetValueTaskSourceCore < RequestContext > _mrvts = new ( )
21
+ {
22
+ // We want to run continuations on the IO threads
23
+ RunContinuationsAsynchronously = false
24
+ } ;
16
25
17
26
private NativeRequestContext _nativeRequestContext ;
18
27
19
28
internal AsyncAcceptContext ( HttpSysListener server )
20
29
{
21
30
Server = server ;
22
- AllocateNativeRequest ( ) ;
31
+ _preallocatedOverlapped = new ( IOCallback , state : this , pinData : null ) ;
23
32
}
24
33
25
34
internal HttpSysListener Server { get ; }
26
35
27
- [ SuppressMessage ( "Microsoft.Design" , "CA1031:DoNotCatchGeneralExceptionTypes" , Justification = "Redirecting to callback" ) ]
28
- [ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Disposed by callback" ) ]
36
+ internal ValueTask < RequestContext > AcceptAsync ( )
37
+ {
38
+ _mrvts . Reset ( ) ;
39
+
40
+ AllocateNativeRequest ( ) ;
41
+
42
+ uint statusCode = QueueBeginGetContext ( ) ;
43
+ if ( statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
44
+ statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_IO_PENDING )
45
+ {
46
+ // some other bad error, possible(?) return values are:
47
+ // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
48
+ return ValueTask . FromException < RequestContext > ( new HttpSysException ( ( int ) statusCode ) ) ;
49
+ }
50
+
51
+ return new ValueTask < RequestContext > ( this , _mrvts . Version ) ;
52
+ }
53
+
29
54
private static void IOCompleted ( AsyncAcceptContext asyncContext , uint errorCode , uint numBytes )
30
55
{
31
56
bool complete = false ;
57
+
32
58
try
33
59
{
34
60
if ( errorCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
35
61
errorCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_MORE_DATA )
36
62
{
37
- asyncContext . TrySetException ( new HttpSysException ( ( int ) errorCode ) ) ;
38
- complete = true ;
63
+ asyncContext . _mrvts . SetException ( new HttpSysException ( ( int ) errorCode ) ) ;
64
+ return ;
39
65
}
40
- else
66
+
67
+ HttpSysListener server = asyncContext . Server ;
68
+ if ( errorCode == UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS )
41
69
{
42
- HttpSysListener server = asyncContext . Server ;
43
- if ( errorCode == UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS )
70
+ // at this point we have received an unmanaged HTTP_REQUEST and memoryBlob
71
+ // points to it we need to hook up our authentication handling code here.
72
+ try
44
73
{
45
- // at this point we have received an unmanaged HTTP_REQUEST and memoryBlob
46
- // points to it we need to hook up our authentication handling code here.
47
- try
48
- {
49
- if ( server . ValidateRequest ( asyncContext . _nativeRequestContext ) && server . ValidateAuth ( asyncContext . _nativeRequestContext ) )
50
- {
51
- RequestContext requestContext = new RequestContext ( server , asyncContext . _nativeRequestContext ) ;
52
- asyncContext . TrySetResult ( requestContext ) ;
53
- complete = true ;
54
- }
55
- }
56
- catch ( Exception )
57
- {
58
- server . SendError ( asyncContext . _nativeRequestContext . RequestId , StatusCodes . Status400BadRequest ) ;
59
- throw ;
60
- }
61
- finally
74
+ var nativeContext = asyncContext . _nativeRequestContext ;
75
+
76
+ if ( server . ValidateRequest ( nativeContext ) && server . ValidateAuth ( nativeContext ) )
62
77
{
63
- // The request has been handed to the user, which means this code can't reuse the blob. Reset it here.
64
- if ( complete )
65
- {
66
- asyncContext . _nativeRequestContext = null ;
67
- }
68
- else
69
- {
70
- asyncContext . AllocateNativeRequest ( size : asyncContext . _nativeRequestContext . Size ) ;
71
- }
78
+ // It's important that we clear the native request context before we set the result
79
+ // we want to reuse this object for future accepts.
80
+ asyncContext . _nativeRequestContext = null ;
81
+
82
+ var requestContext = new RequestContext ( server , nativeContext ) ;
83
+ asyncContext . _mrvts . SetResult ( requestContext ) ;
84
+
85
+ complete = true ;
72
86
}
73
87
}
74
- else
88
+ catch ( Exception ex )
75
89
{
76
- // (uint)backingBuffer.Length - AlignmentPadding
77
- asyncContext . AllocateNativeRequest ( numBytes , asyncContext . _nativeRequestContext . RequestId ) ;
90
+ server . SendError ( asyncContext . _nativeRequestContext . RequestId , StatusCodes . Status400BadRequest ) ;
91
+ asyncContext . _mrvts . SetException ( ex ) ;
78
92
}
79
-
80
- // We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
81
- if ( ! complete )
93
+ finally
82
94
{
83
- uint statusCode = asyncContext . QueueBeginGetContext ( ) ;
84
- if ( statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
85
- statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_IO_PENDING )
95
+ if ( ! complete )
86
96
{
87
- // someother bad error, possible(?) return values are:
88
- // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
89
- asyncContext . TrySetException ( new HttpSysException ( ( int ) statusCode ) ) ;
90
- complete = true ;
97
+ asyncContext . AllocateNativeRequest ( size : asyncContext . _nativeRequestContext . Size ) ;
91
98
}
92
99
}
93
- if ( ! complete )
94
- {
95
- return ;
96
- }
100
+ }
101
+ else
102
+ {
103
+ // (uint)backingBuffer.Length - AlignmentPadding
104
+ asyncContext . AllocateNativeRequest ( numBytes , asyncContext . _nativeRequestContext . RequestId ) ;
97
105
}
98
106
99
- if ( complete )
107
+ // We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
108
+ if ( ! complete )
100
109
{
101
- asyncContext . Dispose ( ) ;
110
+ uint statusCode = asyncContext . QueueBeginGetContext ( ) ;
111
+
112
+ if ( statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
113
+ statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_IO_PENDING )
114
+ {
115
+ // someother bad error, possible(?) return values are:
116
+ // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
117
+ asyncContext . _mrvts . SetException ( new HttpSysException ( ( int ) statusCode ) ) ;
118
+ }
102
119
}
103
120
}
104
121
catch ( Exception exception )
105
122
{
106
- // Logged by caller
107
- asyncContext . TrySetException ( exception ) ;
108
- asyncContext . Dispose ( ) ;
123
+ asyncContext . _mrvts . SetException ( exception ) ;
109
124
}
110
125
}
111
126
112
127
private static unsafe void IOWaitCallback ( uint errorCode , uint numBytes , NativeOverlapped * nativeOverlapped )
113
128
{
114
- // take the ListenerAsyncResult object from the state
115
129
var asyncResult = ( AsyncAcceptContext ) ThreadPoolBoundHandle . GetNativeOverlappedState ( nativeOverlapped ) ;
116
130
IOCompleted ( asyncResult , errorCode , numBytes ) ;
117
131
}
118
132
119
- internal uint QueueBeginGetContext ( )
133
+ private uint QueueBeginGetContext ( )
120
134
{
121
135
uint statusCode = UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS ;
122
136
bool retry ;
@@ -133,7 +147,7 @@ internal uint QueueBeginGetContext()
133
147
_nativeRequestContext . NativeRequest ,
134
148
_nativeRequestContext . Size ,
135
149
& bytesTransferred ,
136
- _nativeRequestContext . NativeOverlapped ) ;
150
+ _overlapped ) ;
137
151
138
152
if ( statusCode == UnsafeNclNativeMethods . ErrorCodes . ERROR_INVALID_PARAMETER && _nativeRequestContext . RequestId != 0 )
139
153
{
@@ -162,18 +176,20 @@ internal uint QueueBeginGetContext()
162
176
return statusCode ;
163
177
}
164
178
165
- internal void AllocateNativeRequest ( uint ? size = null , ulong requestId = 0 )
179
+ private void AllocateNativeRequest ( uint ? size = null , ulong requestId = 0 )
166
180
{
167
181
_nativeRequestContext ? . ReleasePins ( ) ;
168
182
_nativeRequestContext ? . Dispose ( ) ;
169
183
170
- // We can't reuse overlapped objects
171
184
var boundHandle = Server . RequestQueue . BoundHandle ;
172
- var nativeOverlapped = new SafeNativeOverlapped ( boundHandle ,
173
- boundHandle . AllocateNativeOverlapped ( IOCallback , this , pinData : null ) ) ;
174
185
175
- // nativeRequest
176
- _nativeRequestContext = new NativeRequestContext ( nativeOverlapped , Server . MemoryPool , size , requestId ) ;
186
+ if ( _overlapped != null )
187
+ {
188
+ boundHandle . FreeNativeOverlapped ( _overlapped ) ;
189
+ }
190
+
191
+ _nativeRequestContext = new NativeRequestContext ( Server . MemoryPool , size , requestId ) ;
192
+ _overlapped = boundHandle . AllocateNativeOverlapped ( _preallocatedOverlapped ) ;
177
193
}
178
194
179
195
public void Dispose ( )
@@ -189,8 +205,32 @@ protected virtual void Dispose(bool disposing)
189
205
{
190
206
_nativeRequestContext . ReleasePins ( ) ;
191
207
_nativeRequestContext . Dispose ( ) ;
208
+ _nativeRequestContext = null ;
209
+
210
+ var boundHandle = Server . RequestQueue . BoundHandle ;
211
+
212
+ if ( _overlapped != null )
213
+ {
214
+ boundHandle . FreeNativeOverlapped ( _overlapped ) ;
215
+ _overlapped = null ;
216
+ }
192
217
}
193
218
}
194
219
}
220
+
221
+ public RequestContext GetResult ( short token )
222
+ {
223
+ return _mrvts . GetResult ( token ) ;
224
+ }
225
+
226
+ public ValueTaskSourceStatus GetStatus ( short token )
227
+ {
228
+ return _mrvts . GetStatus ( token ) ;
229
+ }
230
+
231
+ public void OnCompleted ( Action < object > continuation , object state , short token , ValueTaskSourceOnCompletedFlags flags )
232
+ {
233
+ _mrvts . OnCompleted ( continuation , state , token , flags ) ;
234
+ }
195
235
}
196
236
}
0 commit comments