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