TaskMethodInvoker.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // <copyright>
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // </copyright>
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System;
  7. using System.Diagnostics;
  8. using System.Reflection;
  9. using System.Runtime;
  10. using System.Runtime.Diagnostics;
  11. using System.Security;
  12. using System.ServiceModel.Description;
  13. using System.ServiceModel.Diagnostics;
  14. using System.ServiceModel.Diagnostics.Application;
  15. using System.Threading.Tasks;
  16. /// <summary>
  17. /// An invoker used when some operation contract has a return value of Task or its generic counterpart (Task of T)
  18. /// </summary>
  19. internal class TaskMethodInvoker : IOperationInvoker
  20. {
  21. private const string ResultMethodName = "Result";
  22. private MethodInfo taskMethod;
  23. private bool isGenericTask;
  24. private InvokeDelegate invokeDelegate;
  25. private int inputParameterCount;
  26. private int outputParameterCount;
  27. private object[] outputs;
  28. private MethodInfo toAsyncMethodInfo;
  29. private MethodInfo taskTResultGetMethod;
  30. public TaskMethodInvoker(MethodInfo taskMethod, Type taskType)
  31. {
  32. if (taskMethod == null)
  33. {
  34. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("taskMethod"));
  35. }
  36. this.taskMethod = taskMethod;
  37. if (taskType != ServiceReflector.VoidType)
  38. {
  39. this.toAsyncMethodInfo = TaskExtensions.MakeGenericMethod(taskType);
  40. this.taskTResultGetMethod = ((PropertyInfo)taskMethod.ReturnType.GetMember(ResultMethodName)[0]).GetGetMethod();
  41. this.isGenericTask = true;
  42. }
  43. }
  44. public bool IsSynchronous
  45. {
  46. get { return false; }
  47. }
  48. public MethodInfo TaskMethod
  49. {
  50. get { return this.taskMethod; }
  51. }
  52. private InvokeDelegate InvokeDelegate
  53. {
  54. get
  55. {
  56. this.EnsureIsInitialized();
  57. return this.invokeDelegate;
  58. }
  59. }
  60. private int InputParameterCount
  61. {
  62. get
  63. {
  64. this.EnsureIsInitialized();
  65. return this.inputParameterCount;
  66. }
  67. }
  68. private int OutputParameterCount
  69. {
  70. get
  71. {
  72. this.EnsureIsInitialized();
  73. return this.outputParameterCount;
  74. }
  75. }
  76. public object[] AllocateInputs()
  77. {
  78. return EmptyArray.Allocate(this.InputParameterCount);
  79. }
  80. public object Invoke(object instance, object[] inputs, out object[] outputs)
  81. {
  82. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
  83. }
  84. public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
  85. {
  86. if (instance == null)
  87. {
  88. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNoServiceObject)));
  89. }
  90. if (inputs == null)
  91. {
  92. if (this.InputParameterCount > 0)
  93. {
  94. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInputParametersToServiceNull, this.InputParameterCount)));
  95. }
  96. }
  97. else if (inputs.Length != this.InputParameterCount)
  98. {
  99. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInputParametersToServiceInvalid, this.InputParameterCount, inputs.Length)));
  100. }
  101. this.outputs = EmptyArray.Allocate(this.OutputParameterCount);
  102. AsyncMethodInvoker.StartOperationInvokePerformanceCounters(this.taskMethod.Name);
  103. IAsyncResult returnValue;
  104. bool callFailed = true;
  105. bool callFaulted = false;
  106. ServiceModelActivity activity = null;
  107. try
  108. {
  109. Activity boundActivity = null;
  110. AsyncMethodInvoker.CreateActivityInfo(ref activity, ref boundActivity);
  111. AsyncMethodInvoker.StartOperationInvokeTrace(this.taskMethod.Name);
  112. using (boundActivity)
  113. {
  114. if (DiagnosticUtility.ShouldUseActivity)
  115. {
  116. string activityName = SR.GetString(SR.ActivityExecuteMethod, this.taskMethod.DeclaringType.FullName, this.taskMethod.Name);
  117. ServiceModelActivity.Start(activity, activityName, ActivityType.ExecuteUserCode);
  118. }
  119. object taskReturnValue = this.InvokeDelegate(instance, inputs, this.outputs);
  120. if (taskReturnValue == null)
  121. {
  122. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("task");
  123. }
  124. else if (this.isGenericTask)
  125. {
  126. returnValue = (IAsyncResult)this.toAsyncMethodInfo.Invoke(null, new object[] { taskReturnValue, callback, state });
  127. }
  128. else
  129. {
  130. returnValue = ((Task)taskReturnValue).AsAsyncResult(callback, state);
  131. }
  132. callFailed = false;
  133. }
  134. }
  135. catch (System.Security.SecurityException e)
  136. {
  137. DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
  138. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException());
  139. }
  140. catch (Exception e)
  141. {
  142. TraceUtility.TraceUserCodeException(e, this.taskMethod);
  143. if (e is FaultException)
  144. {
  145. callFaulted = true;
  146. callFailed = false;
  147. }
  148. throw;
  149. }
  150. finally
  151. {
  152. ServiceModelActivity.Stop(activity);
  153. // Any exception above means InvokeEnd will not be called, so complete it here.
  154. if (callFailed || callFaulted)
  155. {
  156. AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, this.TaskMethod.Name);
  157. AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, this.TaskMethod.Name);
  158. }
  159. }
  160. return returnValue;
  161. }
  162. public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
  163. {
  164. object returnVal;
  165. bool callFailed = true;
  166. bool callFaulted = false;
  167. ServiceModelActivity activity = null;
  168. if (instance == null)
  169. {
  170. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNoServiceObject)));
  171. }
  172. try
  173. {
  174. Activity boundOperation = null;
  175. AsyncMethodInvoker.GetActivityInfo(ref activity, ref boundOperation);
  176. using (boundOperation)
  177. {
  178. Task task = result as Task;
  179. Fx.Assert(task != null, "InvokeEnd needs to be called with the result returned from InvokeBegin.");
  180. if (task.IsFaulted)
  181. {
  182. Fx.Assert(task.Exception != null, "Task.IsFaulted guarantees non-null exception.");
  183. // If FaultException is thrown, we will get 'callFaulted' behavior below.
  184. // Any other exception will retain 'callFailed' behavior.
  185. throw FxTrace.Exception.AsError<FaultException>(task.Exception);
  186. }
  187. // Task cancellation without an exception indicates failure but we have no
  188. // additional information to provide. Accessing Task.Result will throw a
  189. // TaskCanceledException. For consistency between void Tasks and Task<T>,
  190. // we detect and throw here.
  191. if (task.IsCanceled)
  192. {
  193. throw FxTrace.Exception.AsError(new TaskCanceledException(task));
  194. }
  195. outputs = this.outputs;
  196. if (this.isGenericTask)
  197. {
  198. returnVal = this.taskTResultGetMethod.Invoke(result, Type.EmptyTypes);
  199. }
  200. else
  201. {
  202. returnVal = null;
  203. }
  204. callFailed = false;
  205. }
  206. }
  207. catch (SecurityException e)
  208. {
  209. DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
  210. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException());
  211. }
  212. catch (FaultException)
  213. {
  214. callFaulted = true;
  215. callFailed = false;
  216. throw;
  217. }
  218. finally
  219. {
  220. ServiceModelActivity.Stop(activity);
  221. AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, this.TaskMethod.Name);
  222. AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, this.TaskMethod.Name);
  223. }
  224. return returnVal;
  225. }
  226. private void EnsureIsInitialized()
  227. {
  228. if (this.invokeDelegate == null)
  229. {
  230. // Only pass locals byref because InvokerUtil may store temporary results in the byref.
  231. // If two threads both reference this.count, temporary results may interact.
  232. int inputParameterCount;
  233. int outputParameterCount;
  234. InvokeDelegate invokeDelegate = new InvokerUtil().GenerateInvokeDelegate(this.taskMethod, out inputParameterCount, out outputParameterCount);
  235. this.inputParameterCount = inputParameterCount;
  236. this.outputParameterCount = outputParameterCount;
  237. this.invokeDelegate = invokeDelegate; // must set this last due to ----
  238. }
  239. }
  240. }
  241. }