1 //----------------------------------------------------------------------------- 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //----------------------------------------------------------------------------- 4 5 namespace System.Runtime 6 { 7 using System; 8 using System.Diagnostics; 9 using System.Diagnostics.CodeAnalysis; 10 using System.Threading; 11 12 // AsyncResult starts acquired; Complete releases. 13 [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.ManualResetEvent, SupportsAsync = true, ReleaseMethod = "Complete")] 14 abstract class AsyncResult : IAsyncResult 15 { 16 static AsyncCallback asyncCompletionWrapperCallback; 17 AsyncCallback callback; 18 bool completedSynchronously; 19 bool endCalled; 20 Exception exception; 21 bool isCompleted; 22 AsyncCompletion nextAsyncCompletion; 23 object state; 24 Action beforePrepareAsyncCompletionAction; 25 Func<IAsyncResult, bool> checkSyncValidationFunc; 26 27 [Fx.Tag.SynchronizationObject] 28 ManualResetEvent manualResetEvent; 29 30 [Fx.Tag.SynchronizationObject(Blocking = false)] 31 object thisLock; 32 33 #if DEBUG 34 StackTrace endStack; 35 StackTrace completeStack; 36 UncompletedAsyncResultMarker marker; 37 #endif 38 AsyncResult(AsyncCallback callback, object state)39 protected AsyncResult(AsyncCallback callback, object state) 40 { 41 this.callback = callback; 42 this.state = state; 43 this.thisLock = new object(); 44 45 #if DEBUG 46 this.marker = new UncompletedAsyncResultMarker(this); 47 #endif 48 } 49 50 public object AsyncState 51 { 52 get 53 { 54 return state; 55 } 56 } 57 58 public WaitHandle AsyncWaitHandle 59 { 60 get 61 { 62 if (manualResetEvent != null) 63 { 64 return manualResetEvent; 65 } 66 67 lock (ThisLock) 68 { 69 if (manualResetEvent == null) 70 { 71 manualResetEvent = new ManualResetEvent(isCompleted); 72 } 73 } 74 75 return manualResetEvent; 76 } 77 } 78 79 public bool CompletedSynchronously 80 { 81 get 82 { 83 return completedSynchronously; 84 } 85 } 86 87 public bool HasCallback 88 { 89 get 90 { 91 return this.callback != null; 92 } 93 } 94 95 public bool IsCompleted 96 { 97 get 98 { 99 return isCompleted; 100 } 101 } 102 103 // used in conjunction with PrepareAsyncCompletion to allow for finally blocks 104 protected Action<AsyncResult, Exception> OnCompleting { get; set; } 105 106 object ThisLock 107 { 108 get 109 { 110 return this.thisLock; 111 } 112 } 113 114 // subclasses like TraceAsyncResult can use this to wrap the callback functionality in a scope 115 protected Action<AsyncCallback, IAsyncResult> VirtualCallback 116 { 117 get; 118 set; 119 } 120 Complete(bool completedSynchronously)121 protected void Complete(bool completedSynchronously) 122 { 123 if (this.isCompleted) 124 { 125 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultCompletedTwice(GetType()))); 126 } 127 128 #if DEBUG 129 this.marker.AsyncResult = null; 130 this.marker = null; 131 if (!Fx.FastDebug && completeStack == null) 132 { 133 completeStack = new StackTrace(); 134 } 135 #endif 136 137 this.completedSynchronously = completedSynchronously; 138 if (OnCompleting != null) 139 { 140 // Allow exception replacement, like a catch/throw pattern. 141 try 142 { 143 OnCompleting(this, this.exception); 144 } 145 catch (Exception exception) 146 { 147 if (Fx.IsFatal(exception)) 148 { 149 throw; 150 } 151 this.exception = exception; 152 } 153 } 154 155 if (completedSynchronously) 156 { 157 // If we completedSynchronously, then there's no chance that the manualResetEvent was created so 158 // we don't need to worry about a ---- 159 Fx.Assert(this.manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult."); 160 this.isCompleted = true; 161 } 162 else 163 { 164 lock (ThisLock) 165 { 166 this.isCompleted = true; 167 if (this.manualResetEvent != null) 168 { 169 this.manualResetEvent.Set(); 170 } 171 } 172 } 173 174 if (this.callback != null) 175 { 176 try 177 { 178 if (VirtualCallback != null) 179 { 180 VirtualCallback(this.callback, this); 181 } 182 else 183 { 184 this.callback(this); 185 } 186 } 187 #pragma warning disable 1634 188 #pragma warning suppress 56500 // transferring exception to another thread 189 catch (Exception e) 190 { 191 if (Fx.IsFatal(e)) 192 { 193 throw; 194 } 195 196 throw Fx.Exception.AsError(new CallbackException(InternalSR.AsyncCallbackThrewException, e)); 197 } 198 #pragma warning restore 1634 199 } 200 } 201 Complete(bool completedSynchronously, Exception exception)202 protected void Complete(bool completedSynchronously, Exception exception) 203 { 204 this.exception = exception; 205 Complete(completedSynchronously); 206 } 207 AsyncCompletionWrapperCallback(IAsyncResult result)208 static void AsyncCompletionWrapperCallback(IAsyncResult result) 209 { 210 if (result == null) 211 { 212 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult)); 213 } 214 if (result.CompletedSynchronously) 215 { 216 return; 217 } 218 219 AsyncResult thisPtr = (AsyncResult)result.AsyncState; 220 if (!thisPtr.OnContinueAsyncCompletion(result)) 221 { 222 return; 223 } 224 225 AsyncCompletion callback = thisPtr.GetNextCompletion(); 226 if (callback == null) 227 { 228 ThrowInvalidAsyncResult(result); 229 } 230 231 bool completeSelf = false; 232 Exception completionException = null; 233 try 234 { 235 completeSelf = callback(result); 236 } 237 catch (Exception e) 238 { 239 if (Fx.IsFatal(e)) 240 { 241 throw; 242 } 243 completeSelf = true; 244 completionException = e; 245 } 246 247 if (completeSelf) 248 { 249 thisPtr.Complete(false, completionException); 250 } 251 } 252 253 // Note: this should be only derived by the TransactedAsyncResult OnContinueAsyncCompletion(IAsyncResult result)254 protected virtual bool OnContinueAsyncCompletion(IAsyncResult result) 255 { 256 return true; 257 } 258 259 // Note: this should be used only by the TransactedAsyncResult SetBeforePrepareAsyncCompletionAction(Action beforePrepareAsyncCompletionAction)260 protected void SetBeforePrepareAsyncCompletionAction(Action beforePrepareAsyncCompletionAction) 261 { 262 this.beforePrepareAsyncCompletionAction = beforePrepareAsyncCompletionAction; 263 } 264 265 // Note: this should be used only by the TransactedAsyncResult SetCheckSyncValidationFunc(Func<IAsyncResult, bool> checkSyncValidationFunc)266 protected void SetCheckSyncValidationFunc(Func<IAsyncResult, bool> checkSyncValidationFunc) 267 { 268 this.checkSyncValidationFunc = checkSyncValidationFunc; 269 } 270 PrepareAsyncCompletion(AsyncCompletion callback)271 protected AsyncCallback PrepareAsyncCompletion(AsyncCompletion callback) 272 { 273 if (this.beforePrepareAsyncCompletionAction != null) 274 { 275 this.beforePrepareAsyncCompletionAction(); 276 } 277 278 this.nextAsyncCompletion = callback; 279 if (AsyncResult.asyncCompletionWrapperCallback == null) 280 { 281 AsyncResult.asyncCompletionWrapperCallback = Fx.ThunkCallback(new AsyncCallback(AsyncCompletionWrapperCallback)); 282 } 283 return AsyncResult.asyncCompletionWrapperCallback; 284 } 285 CheckSyncContinue(IAsyncResult result)286 protected bool CheckSyncContinue(IAsyncResult result) 287 { 288 AsyncCompletion dummy; 289 return TryContinueHelper(result, out dummy); 290 } 291 SyncContinue(IAsyncResult result)292 protected bool SyncContinue(IAsyncResult result) 293 { 294 AsyncCompletion callback; 295 if (TryContinueHelper(result, out callback)) 296 { 297 return callback(result); 298 } 299 else 300 { 301 return false; 302 } 303 } 304 TryContinueHelper(IAsyncResult result, out AsyncCompletion callback)305 bool TryContinueHelper(IAsyncResult result, out AsyncCompletion callback) 306 { 307 if (result == null) 308 { 309 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult)); 310 } 311 312 callback = null; 313 if (this.checkSyncValidationFunc != null) 314 { 315 if (!this.checkSyncValidationFunc(result)) 316 { 317 return false; 318 } 319 } 320 else if (!result.CompletedSynchronously) 321 { 322 return false; 323 } 324 325 callback = GetNextCompletion(); 326 if (callback == null) 327 { 328 ThrowInvalidAsyncResult("Only call Check/SyncContinue once per async operation (once per PrepareAsyncCompletion)."); 329 } 330 return true; 331 } 332 GetNextCompletion()333 AsyncCompletion GetNextCompletion() 334 { 335 AsyncCompletion result = this.nextAsyncCompletion; 336 this.nextAsyncCompletion = null; 337 return result; 338 } 339 ThrowInvalidAsyncResult(IAsyncResult result)340 protected static void ThrowInvalidAsyncResult(IAsyncResult result) 341 { 342 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidAsyncResultImplementation(result.GetType()))); 343 } 344 ThrowInvalidAsyncResult(string debugText)345 protected static void ThrowInvalidAsyncResult(string debugText) 346 { 347 string message = InternalSR.InvalidAsyncResultImplementationGeneric; 348 if (debugText != null) 349 { 350 #if DEBUG 351 message += " " + debugText; 352 #endif 353 } 354 throw Fx.Exception.AsError(new InvalidOperationException(message)); 355 } 356 357 [Fx.Tag.Blocking(Conditional = "!asyncResult.isCompleted")] 358 protected static TAsyncResult End<TAsyncResult>(IAsyncResult result) 359 where TAsyncResult : AsyncResult 360 { 361 if (result == null) 362 { 363 throw Fx.Exception.ArgumentNull("result"); 364 } 365 366 TAsyncResult asyncResult = result as TAsyncResult; 367 368 if (asyncResult == null) 369 { 370 throw Fx.Exception.Argument("result", InternalSR.InvalidAsyncResult); 371 } 372 373 if (asyncResult.endCalled) 374 { 375 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultAlreadyEnded)); 376 } 377 378 #if DEBUG 379 if (!Fx.FastDebug && asyncResult.endStack == null) 380 { 381 asyncResult.endStack = new StackTrace(); 382 } 383 #endif 384 385 asyncResult.endCalled = true; 386 387 if (!asyncResult.isCompleted) 388 { 389 asyncResult.AsyncWaitHandle.WaitOne(); 390 } 391 392 if (asyncResult.manualResetEvent != null) 393 { 394 asyncResult.manualResetEvent.Close(); 395 } 396 397 if (asyncResult.exception != null) 398 { 399 throw Fx.Exception.AsError(asyncResult.exception); 400 } 401 402 return asyncResult; 403 } 404 405 // can be utilized by subclasses to write core completion code for both the sync and async paths 406 // in one location, signalling chainable synchronous completion with the boolean result, 407 // and leveraging PrepareAsyncCompletion for conversion to an AsyncCallback. 408 // NOTE: requires that "this" is passed in as the state object to the asynchronous sub-call being used with a completion routine. AsyncCompletion(IAsyncResult result)409 protected delegate bool AsyncCompletion(IAsyncResult result); 410 411 #if DEBUG 412 class UncompletedAsyncResultMarker 413 { UncompletedAsyncResultMarker(AsyncResult result)414 public UncompletedAsyncResultMarker(AsyncResult result) 415 { 416 AsyncResult = result; 417 } 418 419 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, 420 Justification = "Debug-only facility")] 421 public AsyncResult AsyncResult { get; set; } 422 } 423 #endif 424 } 425 } 426