AsyncCallBase.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. #region Copyright notice and license
  2. // Copyright 2015 gRPC authors.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #endregion
  16. using System;
  17. using System.Diagnostics;
  18. using System.IO;
  19. using System.Runtime.CompilerServices;
  20. using System.Runtime.InteropServices;
  21. using System.Threading;
  22. using System.Threading.Tasks;
  23. using Grpc.Core.Internal;
  24. using Grpc.Core.Logging;
  25. using Grpc.Core.Profiling;
  26. using Grpc.Core.Utils;
  27. namespace Grpc.Core.Internal
  28. {
  29. /// <summary>
  30. /// Base for handling both client side and server side calls.
  31. /// Manages native call lifecycle and provides convenience methods.
  32. /// </summary>
  33. internal abstract class AsyncCallBase<TWrite, TRead> : IReceivedMessageCallback, ISendCompletionCallback
  34. {
  35. static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCallBase<TWrite, TRead>>();
  36. protected static readonly Status DeserializeResponseFailureStatus = new Status(StatusCode.Internal, "Failed to deserialize response message.");
  37. readonly Func<TWrite, byte[]> serializer;
  38. readonly Func<byte[], TRead> deserializer;
  39. protected readonly object myLock = new object();
  40. protected INativeCall call;
  41. protected bool disposed;
  42. protected bool started;
  43. protected bool cancelRequested;
  44. protected TaskCompletionSource<TRead> streamingReadTcs; // Completion of a pending streaming read if not null.
  45. protected TaskCompletionSource<object> streamingWriteTcs; // Completion of a pending streaming write or send close from client if not null.
  46. protected TaskCompletionSource<object> sendStatusFromServerTcs;
  47. protected bool isStreamingWriteCompletionDelayed; // Only used for the client side.
  48. protected bool readingDone; // True if last read (i.e. read with null payload) was already received.
  49. protected bool halfcloseRequested; // True if send close have been initiated.
  50. protected bool finished; // True if close has been received from the peer.
  51. protected bool initialMetadataSent;
  52. protected long streamingWritesCounter; // Number of streaming send operations started so far.
  53. public AsyncCallBase(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
  54. {
  55. this.serializer = GrpcPreconditions.CheckNotNull(serializer);
  56. this.deserializer = GrpcPreconditions.CheckNotNull(deserializer);
  57. }
  58. /// <summary>
  59. /// Requests cancelling the call.
  60. /// </summary>
  61. public void Cancel()
  62. {
  63. lock (myLock)
  64. {
  65. GrpcPreconditions.CheckState(started);
  66. cancelRequested = true;
  67. if (!disposed)
  68. {
  69. call.Cancel();
  70. }
  71. }
  72. }
  73. /// <summary>
  74. /// Requests cancelling the call with given status.
  75. /// </summary>
  76. protected void CancelWithStatus(Status status)
  77. {
  78. lock (myLock)
  79. {
  80. cancelRequested = true;
  81. if (!disposed)
  82. {
  83. call.CancelWithStatus(status);
  84. }
  85. }
  86. }
  87. protected void InitializeInternal(INativeCall call)
  88. {
  89. lock (myLock)
  90. {
  91. this.call = call;
  92. }
  93. }
  94. /// <summary>
  95. /// Initiates sending a message. Only one send operation can be active at a time.
  96. /// </summary>
  97. protected Task SendMessageInternalAsync(TWrite msg, WriteFlags writeFlags)
  98. {
  99. byte[] payload = UnsafeSerialize(msg);
  100. lock (myLock)
  101. {
  102. GrpcPreconditions.CheckState(started);
  103. var earlyResult = CheckSendAllowedOrEarlyResult();
  104. if (earlyResult != null)
  105. {
  106. return earlyResult;
  107. }
  108. call.StartSendMessage(SendCompletionCallback, payload, writeFlags, !initialMetadataSent);
  109. initialMetadataSent = true;
  110. streamingWritesCounter++;
  111. streamingWriteTcs = new TaskCompletionSource<object>();
  112. return streamingWriteTcs.Task;
  113. }
  114. }
  115. /// <summary>
  116. /// Initiates reading a message. Only one read operation can be active at a time.
  117. /// </summary>
  118. protected Task<TRead> ReadMessageInternalAsync()
  119. {
  120. lock (myLock)
  121. {
  122. GrpcPreconditions.CheckState(started);
  123. if (readingDone)
  124. {
  125. // the last read that returns null or throws an exception is idempotent
  126. // and maintains its state.
  127. GrpcPreconditions.CheckState(streamingReadTcs != null, "Call does not support streaming reads.");
  128. return streamingReadTcs.Task;
  129. }
  130. GrpcPreconditions.CheckState(streamingReadTcs == null, "Only one read can be pending at a time");
  131. GrpcPreconditions.CheckState(!disposed);
  132. call.StartReceiveMessage(ReceivedMessageCallback);
  133. streamingReadTcs = new TaskCompletionSource<TRead>();
  134. return streamingReadTcs.Task;
  135. }
  136. }
  137. /// <summary>
  138. /// If there are no more pending actions and no new actions can be started, releases
  139. /// the underlying native resources.
  140. /// </summary>
  141. protected bool ReleaseResourcesIfPossible()
  142. {
  143. if (!disposed && call != null)
  144. {
  145. bool noMoreSendCompletions = streamingWriteTcs == null && (halfcloseRequested || cancelRequested || finished);
  146. if (noMoreSendCompletions && readingDone && finished)
  147. {
  148. ReleaseResources();
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. protected abstract bool IsClient
  155. {
  156. get;
  157. }
  158. /// <summary>
  159. /// Returns an exception to throw for a failed send operation.
  160. /// It is only allowed to call this method for a call that has already finished.
  161. /// </summary>
  162. protected abstract Exception GetRpcExceptionClientOnly();
  163. private void ReleaseResources()
  164. {
  165. if (call != null)
  166. {
  167. call.Dispose();
  168. }
  169. disposed = true;
  170. OnAfterReleaseResources();
  171. }
  172. protected virtual void OnAfterReleaseResources()
  173. {
  174. }
  175. /// <summary>
  176. /// Checks if sending is allowed and possibly returns a Task that allows short-circuiting the send
  177. /// logic by directly returning the write operation result task. Normally, null is returned.
  178. /// </summary>
  179. protected abstract Task CheckSendAllowedOrEarlyResult();
  180. protected byte[] UnsafeSerialize(TWrite msg)
  181. {
  182. return serializer(msg);
  183. }
  184. protected Exception TryDeserialize(byte[] payload, out TRead msg)
  185. {
  186. try
  187. {
  188. msg = deserializer(payload);
  189. return null;
  190. }
  191. catch (Exception e)
  192. {
  193. msg = default(TRead);
  194. return e;
  195. }
  196. }
  197. /// <summary>
  198. /// Handles send completion (including SendCloseFromClient).
  199. /// </summary>
  200. protected void HandleSendFinished(bool success)
  201. {
  202. bool delayCompletion = false;
  203. TaskCompletionSource<object> origTcs = null;
  204. lock (myLock)
  205. {
  206. if (!success && !finished && IsClient) {
  207. // We should be setting this only once per call, following writes will be short circuited
  208. // because they cannot start until the entire call finishes.
  209. GrpcPreconditions.CheckState(!isStreamingWriteCompletionDelayed);
  210. // leave streamingWriteTcs set, it will be completed once call finished.
  211. isStreamingWriteCompletionDelayed = true;
  212. delayCompletion = true;
  213. }
  214. else
  215. {
  216. origTcs = streamingWriteTcs;
  217. streamingWriteTcs = null;
  218. }
  219. ReleaseResourcesIfPossible();
  220. }
  221. if (!success)
  222. {
  223. if (!delayCompletion)
  224. {
  225. if (IsClient)
  226. {
  227. GrpcPreconditions.CheckState(finished); // implied by !success && !delayCompletion && IsClient
  228. origTcs.SetException(GetRpcExceptionClientOnly());
  229. }
  230. else
  231. {
  232. origTcs.SetException (new IOException("Error sending from server."));
  233. }
  234. }
  235. // if delayCompletion == true, postpone SetException until call finishes.
  236. }
  237. else
  238. {
  239. origTcs.SetResult(null);
  240. }
  241. }
  242. /// <summary>
  243. /// Handles send status from server completion.
  244. /// </summary>
  245. protected void HandleSendStatusFromServerFinished(bool success)
  246. {
  247. lock (myLock)
  248. {
  249. ReleaseResourcesIfPossible();
  250. }
  251. if (!success)
  252. {
  253. sendStatusFromServerTcs.SetException(new IOException("Error sending status from server."));
  254. }
  255. else
  256. {
  257. sendStatusFromServerTcs.SetResult(null);
  258. }
  259. }
  260. /// <summary>
  261. /// Handles streaming read completion.
  262. /// </summary>
  263. protected void HandleReadFinished(bool success, byte[] receivedMessage)
  264. {
  265. // if success == false, received message will be null. It that case we will
  266. // treat this completion as the last read an rely on C core to handle the failed
  267. // read (e.g. deliver approriate statusCode on the clientside).
  268. TRead msg = default(TRead);
  269. var deserializeException = (success && receivedMessage != null) ? TryDeserialize(receivedMessage, out msg) : null;
  270. TaskCompletionSource<TRead> origTcs = null;
  271. lock (myLock)
  272. {
  273. origTcs = streamingReadTcs;
  274. if (receivedMessage == null)
  275. {
  276. // This was the last read.
  277. readingDone = true;
  278. }
  279. if (deserializeException != null && IsClient)
  280. {
  281. readingDone = true;
  282. // TODO(jtattermusch): it might be too late to set the status
  283. CancelWithStatus(DeserializeResponseFailureStatus);
  284. }
  285. if (!readingDone)
  286. {
  287. streamingReadTcs = null;
  288. }
  289. ReleaseResourcesIfPossible();
  290. }
  291. if (deserializeException != null && !IsClient)
  292. {
  293. origTcs.SetException(new IOException("Failed to deserialize request message.", deserializeException));
  294. return;
  295. }
  296. origTcs.SetResult(msg);
  297. }
  298. protected ISendCompletionCallback SendCompletionCallback => this;
  299. void ISendCompletionCallback.OnSendCompletion(bool success)
  300. {
  301. HandleSendFinished(success);
  302. }
  303. IReceivedMessageCallback ReceivedMessageCallback => this;
  304. void IReceivedMessageCallback.OnReceivedMessage(bool success, byte[] receivedMessage)
  305. {
  306. HandleReadFinished(success, receivedMessage);
  307. }
  308. }
  309. }