aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
blob: 28135d7b0d5ab959e7d5135338dfc73399b3e269 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Grpc.Core.Profiling;

using Grpc.Core.Utils;

namespace Grpc.Core.Internal
{
    /// <summary>
    /// grpc_completion_queue from <c>grpc/grpc.h</c>
    /// </summary>
    internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
    {
        static readonly NativeMethods Native = NativeMethods.Get();

        AtomicCounter shutdownRefcount = new AtomicCounter(1);
        CompletionRegistry completionRegistry;

        private CompletionQueueSafeHandle()
        {
        }

        /// <summary>
        /// Create a completion queue that can only be used for Pluck operations.
        /// </summary>
        public static CompletionQueueSafeHandle CreateSync()
        {
            return Native.grpcsharp_completion_queue_create_sync();
        }

        /// <summary>
        /// Create a completion queue that can only be used for Next operations.
        /// </summary>
        public static CompletionQueueSafeHandle CreateAsync(CompletionRegistry completionRegistry)
        {
            var cq = Native.grpcsharp_completion_queue_create_async();
            cq.completionRegistry = completionRegistry;
            return cq;
        }

        public CompletionQueueEvent Next()
        {
            return Native.grpcsharp_completion_queue_next(this);
        }

        public CompletionQueueEvent Pluck(IntPtr tag)
        {
            return Native.grpcsharp_completion_queue_pluck(this, tag);
        }

        /// <summary>
        /// Creates a new usage scope for this completion queue. Once successfully created,
        /// the completion queue won't be shutdown before scope.Dispose() is called.
        /// </summary>
        public UsageScope NewScope()
        {
            return new UsageScope(this);
        }

        public void Shutdown()
        {
            DecrementShutdownRefcount();
        }

        /// <summary>
        /// Completion registry associated with this completion queue.
        /// Doesn't need to be set if only using Pluck() operations.
        /// </summary>
        public CompletionRegistry CompletionRegistry
        {
            get { return completionRegistry; }
        }

        protected override bool ReleaseHandle()
        {
            Native.grpcsharp_completion_queue_destroy(handle);
            return true;
        }

        private void DecrementShutdownRefcount()
        {
            if (shutdownRefcount.Decrement() == 0)
            {
                Native.grpcsharp_completion_queue_shutdown(this);
            }
        }

        private void BeginOp()
        {
            bool success = false;
            shutdownRefcount.IncrementIfNonzero(ref success);
            GrpcPreconditions.CheckState(success, "Shutdown has already been called");
        }

        private void EndOp()
        {
            DecrementShutdownRefcount();
        }

        // Allows declaring BeginOp and EndOp of a completion queue with a using statement.
        // Declared as struct for better performance.
        public struct UsageScope : IDisposable
        {
            readonly CompletionQueueSafeHandle cq;

            public UsageScope(CompletionQueueSafeHandle cq)
            {
                this.cq = cq;
                this.cq.BeginOp();
            }

            public void Dispose()
            {
                cq.EndOp();
            }
        }
    }
}