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 #endregion
16 using System;
17 using System.Runtime.InteropServices;
18 using System.Threading.Tasks;
19 using Grpc.Core.Profiling;
20 
21 using Grpc.Core.Utils;
22 
23 namespace Grpc.Core.Internal
24 {
25     /// <summary>
26     /// grpc_completion_queue from <c>grpc/grpc.h</c>
27     /// </summary>
28     internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
29     {
30         static readonly NativeMethods Native = NativeMethods.Get();
31 
32         AtomicCounter shutdownRefcount = new AtomicCounter(1);
33         CompletionRegistry completionRegistry;
34 
CompletionQueueSafeHandle()35         private CompletionQueueSafeHandle()
36         {
37         }
38 
39         /// <summary>
40         /// Create a completion queue that can only be used for Pluck operations.
41         /// </summary>
CreateSync()42         public static CompletionQueueSafeHandle CreateSync()
43         {
44             return Native.grpcsharp_completion_queue_create_sync();
45         }
46 
47         /// <summary>
48         /// Create a completion queue that can only be used for Next operations.
49         /// </summary>
CreateAsync(CompletionRegistry completionRegistry)50         public static CompletionQueueSafeHandle CreateAsync(CompletionRegistry completionRegistry)
51         {
52             var cq = Native.grpcsharp_completion_queue_create_async();
53             cq.completionRegistry = completionRegistry;
54             return cq;
55         }
56 
Next()57         public CompletionQueueEvent Next()
58         {
59             return Native.grpcsharp_completion_queue_next(this);
60         }
61 
Pluck(IntPtr tag)62         public CompletionQueueEvent Pluck(IntPtr tag)
63         {
64             return Native.grpcsharp_completion_queue_pluck(this, tag);
65         }
66 
67         /// <summary>
68         /// Creates a new usage scope for this completion queue. Once successfully created,
69         /// the completion queue won't be shutdown before scope.Dispose() is called.
70         /// </summary>
NewScope()71         public UsageScope NewScope()
72         {
73             return new UsageScope(this);
74         }
75 
Shutdown()76         public void Shutdown()
77         {
78             DecrementShutdownRefcount();
79         }
80 
81         /// <summary>
82         /// Completion registry associated with this completion queue.
83         /// Doesn't need to be set if only using Pluck() operations.
84         /// </summary>
85         public CompletionRegistry CompletionRegistry
86         {
87             get { return completionRegistry; }
88         }
89 
ReleaseHandle()90         protected override bool ReleaseHandle()
91         {
92             Native.grpcsharp_completion_queue_destroy(handle);
93             return true;
94         }
95 
DecrementShutdownRefcount()96         private void DecrementShutdownRefcount()
97         {
98             if (shutdownRefcount.Decrement() == 0)
99             {
100                 Native.grpcsharp_completion_queue_shutdown(this);
101             }
102         }
103 
BeginOp()104         private void BeginOp()
105         {
106             bool success = false;
107             shutdownRefcount.IncrementIfNonzero(ref success);
108             GrpcPreconditions.CheckState(success, "Shutdown has already been called");
109         }
110 
EndOp()111         private void EndOp()
112         {
113             DecrementShutdownRefcount();
114         }
115 
116         // Allows declaring BeginOp and EndOp of a completion queue with a using statement.
117         // Declared as struct for better performance.
118         public struct UsageScope : IDisposable
119         {
120             readonly CompletionQueueSafeHandle cq;
121 
UsageScopeGrpc.Core.Internal.CompletionQueueSafeHandle.UsageScope122             public UsageScope(CompletionQueueSafeHandle cq)
123             {
124                 this.cq = cq;
125                 this.cq.BeginOp();
126             }
127 
DisposeGrpc.Core.Internal.CompletionQueueSafeHandle.UsageScope128             public void Dispose()
129             {
130                 cq.EndOp();
131             }
132         }
133     }
134 }
135