085e7faf595c36a112ab55a80138a900f90ce03a
[platform/upstream/grpc.git] / src / csharp / Grpc.Core / Internal / BatchContextSafeHandle.cs
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     {
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
44         private BatchContextSafeHandle()
45         {
46         }
47
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
62         public void SetReturnToPoolAction(Action<BatchContextSafeHandle> returnAction)
63         {
64             GrpcPreconditions.CheckState(returnToPoolAction == null);
65             returnToPoolAction = returnAction;
66         }
67
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.
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.
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.
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.
110         public bool GetReceivedCloseOnServerCancelled()
111         {
112             return Native.grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0;
113         }
114
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
133         protected override bool ReleaseHandle()
134         {
135             Native.grpcsharp_batch_context_destroy(handle);
136             return true;
137         }
138
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         {
158             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 }