• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #endregion
17 using System;
18 using System.Diagnostics;
19 using System.Runtime.InteropServices;
20 using System.Text;
21 using Grpc.Core;
22 using Grpc.Core.Utils;
23 using Grpc.Core.Profiling;
24 using System.Buffers;
25 
26 namespace Grpc.Core.Internal
27 {
28     /// <summary>
29     /// grpc_call from <c>grpc/grpc.h</c>
30     /// </summary>
31     internal class CallSafeHandle : SafeHandleZeroIsInvalid, INativeCall
32     {
33         public static readonly CallSafeHandle NullInstance = new CallSafeHandle();
34         static readonly NativeMethods Native = NativeMethods.Get();
35 
36         // Completion handlers are pre-allocated to avoid unnecessary delegate allocations.
37         // The "state" field is used to store the actual callback to invoke.
38         static readonly BatchCompletionDelegate CompletionHandler_IUnaryResponseClientCallback =
39             (success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessageReader(), context.GetReceivedInitialMetadata());
40         static readonly BatchCompletionDelegate CompletionHandler_IReceivedStatusOnClientCallback =
41             (success, context, state) => ((IReceivedStatusOnClientCallback)state).OnReceivedStatusOnClient(success, context.GetReceivedStatusOnClient());
42         static readonly BatchCompletionDelegate CompletionHandler_IReceivedMessageCallback =
43             (success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessageReader());
44         static readonly BatchCompletionDelegate CompletionHandler_IReceivedResponseHeadersCallback =
45             (success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata());
46         static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback =
47             (success, context, state) => ((ISendCompletionCallback)state).OnSendCompletion(success);
48         static readonly BatchCompletionDelegate CompletionHandler_ISendStatusFromServerCompletionCallback =
49             (success, context, state) => ((ISendStatusFromServerCompletionCallback)state).OnSendStatusFromServerCompletion(success);
50         static readonly BatchCompletionDelegate CompletionHandler_IReceivedCloseOnServerCallback =
51             (success, context, state) => ((IReceivedCloseOnServerCallback)state).OnReceivedCloseOnServer(success, context.GetReceivedCloseOnServerCancelled());
52 
53         const uint GRPC_WRITE_BUFFER_HINT = 1;
54         CompletionQueueSafeHandle completionQueue;
55 
CallSafeHandle()56         private CallSafeHandle()
57         {
58         }
59 
Initialize(CompletionQueueSafeHandle completionQueue)60         public void Initialize(CompletionQueueSafeHandle completionQueue)
61         {
62             this.completionQueue = completionQueue;
63         }
64 
SetCredentials(CallCredentialsSafeHandle credentials)65         public void SetCredentials(CallCredentialsSafeHandle credentials)
66         {
67             Native.grpcsharp_call_set_credentials(this, credentials).CheckOk();
68         }
69 
StartUnary(IUnaryResponseClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)70         public void StartUnary(IUnaryResponseClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
71         {
72             using (completionQueue.NewScope())
73             {
74                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IUnaryResponseClientCallback, callback);
75                 Native.grpcsharp_call_start_unary(this, ctx, payload, writeFlags, metadataArray, callFlags)
76                     .CheckOk();
77             }
78         }
79 
StartUnary(BatchContextSafeHandle ctx, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)80         public void StartUnary(BatchContextSafeHandle ctx, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
81         {
82             Native.grpcsharp_call_start_unary(this, ctx, payload, writeFlags, metadataArray, callFlags)
83                 .CheckOk();
84         }
85 
StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)86         public void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
87         {
88             using (completionQueue.NewScope())
89             {
90                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IUnaryResponseClientCallback, callback);
91                 Native.grpcsharp_call_start_client_streaming(this, ctx, metadataArray, callFlags).CheckOk();
92             }
93         }
94 
StartServerStreaming(IReceivedStatusOnClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)95         public void StartServerStreaming(IReceivedStatusOnClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
96         {
97             using (completionQueue.NewScope())
98             {
99                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedStatusOnClientCallback, callback);
100                 Native.grpcsharp_call_start_server_streaming(this, ctx, payload, writeFlags, metadataArray, callFlags).CheckOk();
101             }
102         }
103 
StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)104         public void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
105         {
106             using (completionQueue.NewScope())
107             {
108                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedStatusOnClientCallback, callback);
109                 Native.grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray, callFlags).CheckOk();
110             }
111         }
112 
StartSendMessage(ISendCompletionCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)113         public void StartSendMessage(ISendCompletionCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
114         {
115             using (completionQueue.NewScope())
116             {
117                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback);
118                 Native.grpcsharp_call_send_message(this, ctx, payload, writeFlags, sendEmptyInitialMetadata ? 1 : 0).CheckOk();
119             }
120         }
121 
StartSendCloseFromClient(ISendCompletionCallback callback)122         public void StartSendCloseFromClient(ISendCompletionCallback callback)
123         {
124             using (completionQueue.NewScope())
125             {
126                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback);
127                 Native.grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
128             }
129         }
130 
StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, SliceBufferSafeHandle optionalPayload, WriteFlags writeFlags)131         public void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata,
132             SliceBufferSafeHandle optionalPayload, WriteFlags writeFlags)
133         {
134             using (completionQueue.NewScope())
135             {
136                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendStatusFromServerCompletionCallback, callback);
137 
138                 const int MaxStackAllocBytes = 256;
139                 int maxBytes = MarshalUtils.GetMaxByteCountUTF8(status.Detail);
140                 if (maxBytes > MaxStackAllocBytes)
141                 {
142                     // pay the extra to get the *actual* size; this could mean that
143                     // it ends up fitting on the stack after all, but even if not
144                     // it will mean that we ask for a *much* smaller buffer
145                     maxBytes = MarshalUtils.GetByteCountUTF8(status.Detail);
146                 }
147 
148                 unsafe
149                 {
150                     if (maxBytes <= MaxStackAllocBytes)
151                     {   // for small status, we can encode on the stack without touching arrays
152                         // note: if init-locals is disabled, it would be more efficient
153                         // to just stackalloc[MaxStackAllocBytes]; but by default, since we
154                         // expect this to be small and it needs to wipe, just use maxBytes
155                         byte* ptr = stackalloc byte[maxBytes];
156                         int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes);
157                         Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0,
158                             optionalPayload, writeFlags).CheckOk();
159                     }
160                     else
161                     {   // for larger status (rare), rent a buffer from the pool and
162                         // use that for encoding
163                         var statusBuffer = ArrayPool<byte>.Shared.Rent(maxBytes);
164                         try
165                         {
166                             fixed (byte* ptr = statusBuffer)
167                             {
168                                 int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes);
169                                 Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0,
170                                   optionalPayload, writeFlags).CheckOk();
171                             }
172                         }
173                         finally
174                         {
175                             ArrayPool<byte>.Shared.Return(statusBuffer);
176                         }
177                     }
178                 }
179             }
180         }
181 
StartReceiveMessage(IReceivedMessageCallback callback)182         public void StartReceiveMessage(IReceivedMessageCallback callback)
183         {
184             using (completionQueue.NewScope())
185             {
186                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedMessageCallback, callback);
187                 Native.grpcsharp_call_recv_message(this, ctx).CheckOk();
188             }
189         }
190 
StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback)191         public void StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback)
192         {
193             using (completionQueue.NewScope())
194             {
195                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedResponseHeadersCallback, callback);
196                 Native.grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk();
197             }
198         }
199 
StartServerSide(IReceivedCloseOnServerCallback callback)200         public void StartServerSide(IReceivedCloseOnServerCallback callback)
201         {
202             using (completionQueue.NewScope())
203             {
204                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedCloseOnServerCallback, callback);
205                 Native.grpcsharp_call_start_serverside(this, ctx).CheckOk();
206             }
207         }
208 
StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray)209         public void StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray)
210         {
211             using (completionQueue.NewScope())
212             {
213                 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback);
214                 Native.grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk();
215             }
216         }
217 
Cancel()218         public void Cancel()
219         {
220             Native.grpcsharp_call_cancel(this).CheckOk();
221         }
222 
CancelWithStatus(Status status)223         public void CancelWithStatus(Status status)
224         {
225             Native.grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk();
226         }
227 
GetPeer()228         public string GetPeer()
229         {
230             using (var cstring = Native.grpcsharp_call_get_peer(this))
231             {
232                 return cstring.GetValue();
233             }
234         }
235 
GetAuthContext()236         public AuthContextSafeHandle GetAuthContext()
237         {
238             return Native.grpcsharp_call_auth_context(this);
239         }
240 
ReleaseHandle()241         protected override bool ReleaseHandle()
242         {
243             Native.grpcsharp_call_destroy(handle);
244             return true;
245         }
246 
GetFlags(bool buffered)247         private static uint GetFlags(bool buffered)
248         {
249             return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
250         }
251 
252         /// <summary>
253         /// Only for testing.
254         /// </summary>
CreateFake(IntPtr ptr, CompletionQueueSafeHandle cq)255         public static CallSafeHandle CreateFake(IntPtr ptr, CompletionQueueSafeHandle cq)
256         {
257             var call = new CallSafeHandle();
258             call.SetHandle(ptr);
259             call.Initialize(cq);
260             return call;
261         }
262     }
263 }
264