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 25 namespace Grpc.Core.Internal 26 { 27 /// <summary> 28 /// grpc_call from <c>grpc/grpc.h</c> 29 /// </summary> 30 internal class CallSafeHandle : SafeHandleZeroIsInvalid, INativeCall 31 { 32 public static readonly CallSafeHandle NullInstance = new CallSafeHandle(); 33 static readonly NativeMethods Native = NativeMethods.Get(); 34 35 // Completion handlers are pre-allocated to avoid unneccessary delegate allocations. 36 // The "state" field is used to store the actual callback to invoke. 37 static readonly BatchCompletionDelegate CompletionHandler_IUnaryResponseClientCallback = 38 (success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()); 39 static readonly BatchCompletionDelegate CompletionHandler_IReceivedStatusOnClientCallback = 40 (success, context, state) => ((IReceivedStatusOnClientCallback)state).OnReceivedStatusOnClient(success, context.GetReceivedStatusOnClient()); 41 static readonly BatchCompletionDelegate CompletionHandler_IReceivedMessageCallback = 42 (success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessage()); 43 static readonly BatchCompletionDelegate CompletionHandler_IReceivedResponseHeadersCallback = 44 (success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata()); 45 static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback = 46 (success, context, state) => ((ISendCompletionCallback)state).OnSendCompletion(success); 47 static readonly BatchCompletionDelegate CompletionHandler_ISendStatusFromServerCompletionCallback = 48 (success, context, state) => ((ISendStatusFromServerCompletionCallback)state).OnSendStatusFromServerCompletion(success); 49 static readonly BatchCompletionDelegate CompletionHandler_IReceivedCloseOnServerCallback = 50 (success, context, state) => ((IReceivedCloseOnServerCallback)state).OnReceivedCloseOnServer(success, context.GetReceivedCloseOnServerCancelled()); 51 52 const uint GRPC_WRITE_BUFFER_HINT = 1; 53 CompletionQueueSafeHandle completionQueue; 54 CallSafeHandle()55 private CallSafeHandle() 56 { 57 } 58 Initialize(CompletionQueueSafeHandle completionQueue)59 public void Initialize(CompletionQueueSafeHandle completionQueue) 60 { 61 this.completionQueue = completionQueue; 62 } 63 SetCredentials(CallCredentialsSafeHandle credentials)64 public void SetCredentials(CallCredentialsSafeHandle credentials) 65 { 66 Native.grpcsharp_call_set_credentials(this, credentials).CheckOk(); 67 } 68 StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)69 public void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) 70 { 71 using (completionQueue.NewScope()) 72 { 73 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IUnaryResponseClientCallback, callback); 74 Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags) 75 .CheckOk(); 76 } 77 } 78 StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)79 public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) 80 { 81 Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags) 82 .CheckOk(); 83 } 84 StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)85 public void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) 86 { 87 using (completionQueue.NewScope()) 88 { 89 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IUnaryResponseClientCallback, callback); 90 Native.grpcsharp_call_start_client_streaming(this, ctx, metadataArray, callFlags).CheckOk(); 91 } 92 } 93 StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)94 public void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) 95 { 96 using (completionQueue.NewScope()) 97 { 98 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedStatusOnClientCallback, callback); 99 Native.grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags).CheckOk(); 100 } 101 } 102 StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)103 public void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) 104 { 105 using (completionQueue.NewScope()) 106 { 107 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedStatusOnClientCallback, callback); 108 Native.grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray, callFlags).CheckOk(); 109 } 110 } 111 StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)112 public void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) 113 { 114 using (completionQueue.NewScope()) 115 { 116 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback); 117 Native.grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata ? 1 : 0).CheckOk(); 118 } 119 } 120 StartSendCloseFromClient(ISendCompletionCallback callback)121 public void StartSendCloseFromClient(ISendCompletionCallback callback) 122 { 123 using (completionQueue.NewScope()) 124 { 125 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback); 126 Native.grpcsharp_call_send_close_from_client(this, ctx).CheckOk(); 127 } 128 } 129 StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags)130 public void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, 131 byte[] optionalPayload, WriteFlags writeFlags) 132 { 133 using (completionQueue.NewScope()) 134 { 135 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendStatusFromServerCompletionCallback, callback); 136 var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero; 137 var statusDetailBytes = MarshalUtils.GetBytesUTF8(status.Detail); 138 Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata ? 1 : 0, 139 optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); 140 } 141 } 142 StartReceiveMessage(IReceivedMessageCallback callback)143 public void StartReceiveMessage(IReceivedMessageCallback callback) 144 { 145 using (completionQueue.NewScope()) 146 { 147 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedMessageCallback, callback); 148 Native.grpcsharp_call_recv_message(this, ctx).CheckOk(); 149 } 150 } 151 StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback)152 public void StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback) 153 { 154 using (completionQueue.NewScope()) 155 { 156 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedResponseHeadersCallback, callback); 157 Native.grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk(); 158 } 159 } 160 StartServerSide(IReceivedCloseOnServerCallback callback)161 public void StartServerSide(IReceivedCloseOnServerCallback callback) 162 { 163 using (completionQueue.NewScope()) 164 { 165 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedCloseOnServerCallback, callback); 166 Native.grpcsharp_call_start_serverside(this, ctx).CheckOk(); 167 } 168 } 169 StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray)170 public void StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray) 171 { 172 using (completionQueue.NewScope()) 173 { 174 var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback); 175 Native.grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk(); 176 } 177 } 178 Cancel()179 public void Cancel() 180 { 181 Native.grpcsharp_call_cancel(this).CheckOk(); 182 } 183 CancelWithStatus(Status status)184 public void CancelWithStatus(Status status) 185 { 186 Native.grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk(); 187 } 188 GetPeer()189 public string GetPeer() 190 { 191 using (var cstring = Native.grpcsharp_call_get_peer(this)) 192 { 193 return cstring.GetValue(); 194 } 195 } 196 GetAuthContext()197 public AuthContextSafeHandle GetAuthContext() 198 { 199 return Native.grpcsharp_call_auth_context(this); 200 } 201 ReleaseHandle()202 protected override bool ReleaseHandle() 203 { 204 Native.grpcsharp_call_destroy(handle); 205 return true; 206 } 207 GetFlags(bool buffered)208 private static uint GetFlags(bool buffered) 209 { 210 return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; 211 } 212 213 /// <summary> 214 /// Only for testing. 215 /// </summary> CreateFake(IntPtr ptr, CompletionQueueSafeHandle cq)216 public static CallSafeHandle CreateFake(IntPtr ptr, CompletionQueueSafeHandle cq) 217 { 218 var call = new CallSafeHandle(); 219 call.SetHandle(ptr); 220 call.Initialize(cq); 221 return call; 222 } 223 } 224 } 225