PageRenderTime 49ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/WCFWebApi/src/Microsoft.Server.Common/Microsoft/Server/Common/AsyncResult.cs

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