@@ -5,57 +5,55 @@ namespace DotNetty.Common.Concurrency
5
5
{
6
6
using System ;
7
7
using System . Collections . Generic ;
8
+ using System . Diagnostics . Contracts ;
9
+ using System . Reflection ;
8
10
using System . Runtime . CompilerServices ;
9
11
using System . Runtime . ExceptionServices ;
12
+ using System . Runtime . InteropServices . ComTypes ;
10
13
using System . Threading ;
11
14
using System . Threading . Tasks ;
12
15
using System . Threading . Tasks . Sources ;
13
16
14
17
public abstract class AbstractPromise : IPromise , IValueTaskSource
15
18
{
16
- struct CompletionData
17
- {
18
- public Action < object > Continuation { get ; }
19
- public object State { get ; }
20
- public ExecutionContext ExecutionContext { get ; }
21
- public SynchronizationContext SynchronizationContext { get ; }
22
-
23
- public CompletionData ( Action < object > continuation , object state , ExecutionContext executionContext , SynchronizationContext synchronizationContext )
24
- {
25
- this . Continuation = continuation ;
26
- this . State = state ;
27
- this . ExecutionContext = executionContext ;
28
- this . SynchronizationContext = synchronizationContext ;
29
- }
30
- }
31
-
32
- const short SourceToken = 0 ;
33
-
34
19
static readonly ContextCallback ExecutionContextCallback = Execute ;
35
- static readonly SendOrPostCallback SyncContextCallbackWithExecutionContext = ExecuteWithExecutionContext ;
36
20
static readonly SendOrPostCallback SyncContextCallback = Execute ;
21
+ static readonly SendOrPostCallback SyncContextCallbackWithExecutionContext = ExecuteWithExecutionContext ;
22
+ static readonly Action < object > TaskSchedulerCallback = Execute ;
23
+ static readonly Action < object > TaskScheduleCallbackWithExecutionContext = ExecuteWithExecutionContext ;
37
24
38
- static readonly Exception CanceledException = new OperationCanceledException ( ) ;
39
- static readonly Exception CompletedNoException = new Exception ( ) ;
25
+ static readonly Exception CompletedSentinel = new Exception ( ) ;
40
26
27
+ short currentId ;
41
28
protected Exception exception ;
29
+
30
+ Action < object > continuation ;
31
+ object state ;
32
+ ExecutionContext executionContext ;
33
+ object schedulingContext ;
34
+
35
+ public ValueTask ValueTask => new ValueTask ( this , this . currentId ) ;
42
36
43
- int callbackCount ;
44
- CompletionData [ ] completions ;
45
-
46
- public bool TryComplete ( ) => this . TryComplete0 ( CompletedNoException ) ;
37
+ public bool TryComplete ( ) => this . TryComplete0 ( CompletedSentinel , out _ ) ;
47
38
48
- public bool TrySetException ( Exception exception ) => this . TryComplete0 ( exception ) ;
39
+ public bool TrySetException ( Exception exception ) => this . TryComplete0 ( exception , out _ ) ;
49
40
50
- public bool TrySetCanceled ( ) => this . TryComplete0 ( CanceledException ) ;
41
+ public bool TrySetCanceled ( CancellationToken cancellationToken = default ( CancellationToken ) ) => this . TryComplete0 ( new OperationCanceledException ( cancellationToken ) , out _ ) ;
51
42
52
- protected virtual bool TryComplete0 ( Exception exception )
43
+ protected virtual bool TryComplete0 ( Exception exception , out bool continuationInvoked )
53
44
{
45
+ continuationInvoked = false ;
46
+
54
47
if ( this . exception == null )
55
48
{
56
49
// Set the exception object to the exception passed in or a sentinel value
57
50
this . exception = exception ;
58
- this . TryExecuteCompletions ( ) ;
51
+
52
+ if ( this . continuation != null )
53
+ {
54
+ this . ExecuteContinuation ( ) ;
55
+ continuationInvoked = true ;
56
+ }
59
57
return true ;
60
58
}
61
59
@@ -66,15 +64,17 @@ protected virtual bool TryComplete0(Exception exception)
66
64
67
65
public virtual ValueTaskSourceStatus GetStatus ( short token )
68
66
{
67
+ this . EnsureValidToken ( token ) ;
68
+
69
69
if ( this . exception == null )
70
70
{
71
71
return ValueTaskSourceStatus . Pending ;
72
72
}
73
- else if ( this . exception == CompletedNoException )
73
+ else if ( this . exception == CompletedSentinel )
74
74
{
75
75
return ValueTaskSourceStatus . Succeeded ;
76
76
}
77
- else if ( this . exception == CanceledException )
77
+ else if ( this . exception is OperationCanceledException )
78
78
{
79
79
return ValueTaskSourceStatus . Canceled ;
80
80
}
@@ -86,150 +86,117 @@ public virtual ValueTaskSourceStatus GetStatus(short token)
86
86
87
87
public virtual void GetResult ( short token )
88
88
{
89
+ this . EnsureValidToken ( token ) ;
90
+
89
91
if ( this . exception == null )
90
92
{
91
93
throw new InvalidOperationException ( "Attempt to get result on not yet completed promise" ) ;
92
94
}
93
95
94
- this . IsCompletedOrThrow ( ) ;
95
- }
96
+ this . currentId ++ ;
96
97
97
- public virtual void OnCompleted ( Action < object > continuation , object state , short token , ValueTaskSourceOnCompletedFlags flags )
98
- {
99
- if ( this . completions == null )
100
- {
101
- this . completions = new CompletionData [ 1 ] ;
102
- }
103
-
104
- int newIndex = this . callbackCount ;
105
- this . callbackCount ++ ;
106
-
107
- if ( newIndex == this . completions . Length )
108
- {
109
- var newArray = new CompletionData [ this . completions . Length * 2 ] ;
110
- Array . Copy ( this . completions , newArray , this . completions . Length ) ;
111
- this . completions = newArray ;
112
- }
113
-
114
- this . completions [ newIndex ] = new CompletionData (
115
- continuation ,
116
- state ,
117
- ( flags & ValueTaskSourceOnCompletedFlags . FlowExecutionContext ) != 0 ? ExecutionContext . Capture ( ) : null ,
118
- ( flags & ValueTaskSourceOnCompletedFlags . UseSchedulingContext ) != 0 ? SynchronizationContext . Current : null
119
- ) ;
120
-
121
- if ( this . exception != null )
98
+ if ( this . exception != CompletedSentinel )
122
99
{
123
- this . TryExecuteCompletions ( ) ;
100
+ this . ThrowLatchedException ( ) ;
124
101
}
125
102
}
126
103
127
- public static implicit operator ValueTask ( AbstractPromise promise ) => new ValueTask ( promise , SourceToken ) ;
128
-
129
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
130
- bool IsCompletedOrThrow ( )
104
+ public virtual void OnCompleted ( Action < object > continuation , object state , short token , ValueTaskSourceOnCompletedFlags flags )
131
105
{
132
- if ( this . exception == null )
133
- {
134
- return false ;
135
- }
106
+ this . EnsureValidToken ( token ) ;
136
107
137
- if ( this . exception != CompletedNoException )
108
+ if ( this . continuation != null )
138
109
{
139
- this . ThrowLatchedException ( ) ;
110
+ throw new InvalidOperationException ( "Attempt to subscribe same promise twice" ) ;
140
111
}
141
112
142
- return true ;
143
- }
144
-
145
- [ MethodImpl ( MethodImplOptions . NoInlining ) ]
146
- void ThrowLatchedException ( ) => ExceptionDispatchInfo . Capture ( this . exception ) . Throw ( ) ;
147
-
148
- bool TryExecuteCompletions ( )
149
- {
150
- if ( this . callbackCount == 0 || this . completions == null )
151
- {
152
- return false ;
153
- }
113
+ this . continuation = continuation ;
114
+ this . state = state ;
115
+ this . executionContext = ( flags & ValueTaskSourceOnCompletedFlags . FlowExecutionContext ) != 0 ? ExecutionContext . Capture ( ) : null ;
154
116
155
- List < Exception > exceptions = null ;
156
-
157
- for ( int i = 0 ; i < this . callbackCount ; i ++ )
117
+ if ( ( flags & ValueTaskSourceOnCompletedFlags . UseSchedulingContext ) != 0 )
158
118
{
159
- try
119
+ SynchronizationContext sc = SynchronizationContext . Current ;
120
+ if ( sc != null && sc . GetType ( ) != typeof ( SynchronizationContext ) )
160
121
{
161
- CompletionData completion = this . completions [ i ] ;
162
- ExecuteCompletion ( completion ) ;
122
+ this . schedulingContext = sc ;
163
123
}
164
- catch ( Exception ex )
124
+ else
165
125
{
166
- if ( exceptions == null )
126
+ TaskScheduler ts = TaskScheduler . Current ;
127
+ if ( ts != TaskScheduler . Default )
167
128
{
168
- exceptions = new List < Exception > ( ) ;
129
+ this . schedulingContext = ts ;
169
130
}
170
-
171
- exceptions . Add ( ex ) ;
172
131
}
173
132
}
174
133
175
- if ( exceptions = = null )
134
+ if ( this . exception ! = null )
176
135
{
177
- return true ;
136
+ this . ExecuteContinuation ( ) ;
178
137
}
179
-
180
- throw new AggregateException ( exceptions ) ;
181
138
}
139
+
140
+ public static implicit operator ValueTask ( AbstractPromise promise ) => promise . ValueTask ;
141
+
142
+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
143
+ void ThrowLatchedException ( ) => ExceptionDispatchInfo . Capture ( this . exception ) . Throw ( ) ;
182
144
183
145
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
184
- protected void ClearCallbacks ( )
146
+ protected void ClearCallback ( )
185
147
{
186
- if ( this . callbackCount > 0 )
148
+ this . continuation = null ;
149
+ this . state = null ;
150
+ this . executionContext = null ;
151
+ this . schedulingContext = null ;
152
+ }
153
+
154
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
155
+ void EnsureValidToken ( short token )
156
+ {
157
+ if ( this . currentId != token )
187
158
{
188
- this . callbackCount = 0 ;
189
- Array . Clear ( this . completions , 0 , this . completions . Length ) ;
159
+ throw new InvalidOperationException ( "Incorrect ValueTask token" ) ;
190
160
}
191
- }
161
+ }
192
162
193
163
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
194
- static void ExecuteCompletion ( CompletionData completion )
164
+ void ExecuteContinuation ( )
195
165
{
196
- if ( completion . SynchronizationContext == null )
166
+ ExecutionContext executionContext = this . executionContext ;
167
+ object schedulingContext = this . schedulingContext ;
168
+
169
+ if ( schedulingContext == null )
197
170
{
198
- if ( completion . ExecutionContext == null )
171
+ if ( executionContext == null )
199
172
{
200
- completion . Continuation ( completion . State ) ;
173
+ this . ExecuteContinuation0 ( ) ;
201
174
}
202
175
else
203
176
{
204
- //boxing
205
- ExecutionContext . Run ( completion . ExecutionContext , ExecutionContextCallback , completion ) ;
177
+ ExecutionContext . Run ( executionContext , ExecutionContextCallback , this ) ;
206
178
}
207
179
}
180
+ else if ( schedulingContext is SynchronizationContext sc )
181
+ {
182
+ sc . Post ( executionContext == null ? SyncContextCallback : SyncContextCallbackWithExecutionContext , this ) ;
183
+ }
208
184
else
209
185
{
210
- if ( completion . ExecutionContext == null )
211
- {
212
- //boxing
213
- completion . SynchronizationContext . Post ( SyncContextCallback , completion ) ;
214
- }
215
- else
216
- {
217
- //boxing
218
- completion . SynchronizationContext . Post ( SyncContextCallbackWithExecutionContext , completion ) ;
219
- }
186
+ TaskScheduler ts = ( TaskScheduler ) schedulingContext ;
187
+ Contract . Assert ( ts != null , "Expected a TaskScheduler" ) ;
188
+ Task . Factory . StartNew ( executionContext == null ? TaskSchedulerCallback : TaskScheduleCallbackWithExecutionContext , this , CancellationToken . None , TaskCreationOptions . DenyChildAttach , ts ) ;
220
189
}
221
190
}
222
191
223
- static void Execute ( object state )
224
- {
225
- CompletionData completion = ( CompletionData ) state ;
226
- completion . Continuation ( completion . State ) ;
227
- }
228
-
229
- static void ExecuteWithExecutionContext ( object state )
192
+ static void Execute ( object state ) => ( ( AbstractPromise ) state ) . ExecuteContinuation0 ( ) ;
193
+
194
+ static void ExecuteWithExecutionContext ( object state ) => ExecutionContext . Run ( ( ( AbstractPromise ) state ) . executionContext , ExecutionContextCallback , state ) ;
195
+
196
+ protected virtual void ExecuteContinuation0 ( )
230
197
{
231
- CompletionData completion = ( CompletionData ) state ;
232
- ExecutionContext . Run ( completion . ExecutionContext , ExecutionContextCallback , state ) ;
198
+ Contract . Assert ( this . continuation != null ) ;
199
+ this . continuation ( this . state ) ;
233
200
}
234
201
}
235
202
}
0 commit comments