1 #region Copyright notice and license 2 3 // Copyright 2015 gRPC authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System; 20 using System.Runtime.InteropServices; 21 using System.Text; 22 using Grpc.Core; 23 using Grpc.Core.Logging; 24 using Grpc.Core.Utils; 25 26 namespace Grpc.Core.Internal 27 { 28 internal interface IOpCompletionCallback 29 { OnComplete(bool success)30 void OnComplete(bool success); 31 } 32 33 /// <summary> 34 /// grpcsharp_batch_context 35 /// </summary> 36 internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<BatchContextSafeHandle> 37 { 38 static readonly NativeMethods Native = NativeMethods.Get(); 39 static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<BatchContextSafeHandle>(); 40 41 Action<BatchContextSafeHandle> returnToPoolAction; 42 CompletionCallbackData completionCallbackData; 43 BatchContextSafeHandle()44 private BatchContextSafeHandle() 45 { 46 } 47 Create()48 public static BatchContextSafeHandle Create() 49 { 50 var ctx = Native.grpcsharp_batch_context_create(); 51 return ctx; 52 } 53 54 public IntPtr Handle 55 { 56 get 57 { 58 return handle; 59 } 60 } 61 SetReturnToPoolAction(Action<BatchContextSafeHandle> returnAction)62 public void SetReturnToPoolAction(Action<BatchContextSafeHandle> returnAction) 63 { 64 GrpcPreconditions.CheckState(returnToPoolAction == null); 65 returnToPoolAction = returnAction; 66 } 67 SetCompletionCallback(BatchCompletionDelegate callback, object state)68 public void SetCompletionCallback(BatchCompletionDelegate callback, object state) 69 { 70 GrpcPreconditions.CheckState(completionCallbackData.Callback == null); 71 GrpcPreconditions.CheckNotNull(callback, nameof(callback)); 72 completionCallbackData = new CompletionCallbackData(callback, state); 73 } 74 75 // Gets data of recv_initial_metadata completion. GetReceivedInitialMetadata()76 public Metadata GetReceivedInitialMetadata() 77 { 78 IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_initial_metadata(this); 79 return MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); 80 } 81 82 // Gets data of recv_status_on_client completion. GetReceivedStatusOnClient()83 public ClientSideStatus GetReceivedStatusOnClient() 84 { 85 UIntPtr detailsLength; 86 IntPtr detailsPtr = Native.grpcsharp_batch_context_recv_status_on_client_details(this, out detailsLength); 87 string details = MarshalUtils.PtrToStringUTF8(detailsPtr, (int)detailsLength.ToUInt32()); 88 var status = new Status(Native.grpcsharp_batch_context_recv_status_on_client_status(this), details); 89 90 IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_status_on_client_trailing_metadata(this); 91 var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); 92 93 return new ClientSideStatus(status, metadata); 94 } 95 96 // Gets data of recv_message completion. GetReceivedMessage()97 public byte[] GetReceivedMessage() 98 { 99 IntPtr len = Native.grpcsharp_batch_context_recv_message_length(this); 100 if (len == new IntPtr(-1)) 101 { 102 return null; 103 } 104 byte[] data = new byte[(int)len]; 105 Native.grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length)); 106 return data; 107 } 108 109 // Gets data of receive_close_on_server completion. GetReceivedCloseOnServerCancelled()110 public bool GetReceivedCloseOnServerCancelled() 111 { 112 return Native.grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0; 113 } 114 Recycle()115 public void Recycle() 116 { 117 if (returnToPoolAction != null) 118 { 119 Native.grpcsharp_batch_context_reset(this); 120 121 var origReturnAction = returnToPoolAction; 122 // Not clearing all the references to the pool could prevent garbage collection of the pool object 123 // and thus cause memory leaks. 124 returnToPoolAction = null; 125 origReturnAction(this); 126 } 127 else 128 { 129 Dispose(); 130 } 131 } 132 ReleaseHandle()133 protected override bool ReleaseHandle() 134 { 135 Native.grpcsharp_batch_context_destroy(handle); 136 return true; 137 } 138 IOpCompletionCallback.OnComplete(bool success)139 void IOpCompletionCallback.OnComplete(bool success) 140 { 141 try 142 { 143 completionCallbackData.Callback(success, this, completionCallbackData.State); 144 } 145 catch (Exception e) 146 { 147 Logger.Error(e, "Exception occurred while invoking batch completion delegate."); 148 } 149 finally 150 { 151 completionCallbackData = default(CompletionCallbackData); 152 Recycle(); 153 } 154 } 155 156 struct CompletionCallbackData 157 { CompletionCallbackDataGrpc.Core.Internal.BatchContextSafeHandle.CompletionCallbackData158 public CompletionCallbackData(BatchCompletionDelegate callback, object state) 159 { 160 this.Callback = callback; 161 this.State = state; 162 } 163 164 public BatchCompletionDelegate Callback { get; } 165 public object State { get; } 166 } 167 } 168 } 169