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.Collections.Concurrent;
21 using System.Collections.Generic;
22 using System.Diagnostics;
23 using System.Runtime.InteropServices;
24 using System.Threading;
25 using Grpc.Core.Logging;
26 using Grpc.Core.Utils;
27 
28 namespace Grpc.Core.Internal
29 {
BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx, object state)30     internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx, object state);
31 
RequestCallCompletionDelegate(bool success, RequestCallContextSafeHandle ctx)32     internal delegate void RequestCallCompletionDelegate(bool success, RequestCallContextSafeHandle ctx);
33 
34     internal class CompletionRegistry
35     {
36         static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<CompletionRegistry>();
37 
38         readonly GrpcEnvironment environment;
39         readonly Func<BatchContextSafeHandle> batchContextFactory;
40         readonly Func<RequestCallContextSafeHandle> requestCallContextFactory;
41         readonly Dictionary<IntPtr, IOpCompletionCallback> dict = new Dictionary<IntPtr, IOpCompletionCallback>(new IntPtrComparer());
42         SpinLock spinLock = new SpinLock(Debugger.IsAttached);
43         IntPtr lastRegisteredKey;  // only for testing
44 
CompletionRegistry(GrpcEnvironment environment, Func<BatchContextSafeHandle> batchContextFactory, Func<RequestCallContextSafeHandle> requestCallContextFactory)45         public CompletionRegistry(GrpcEnvironment environment, Func<BatchContextSafeHandle> batchContextFactory, Func<RequestCallContextSafeHandle> requestCallContextFactory)
46         {
47             this.environment = GrpcPreconditions.CheckNotNull(environment);
48             this.batchContextFactory = GrpcPreconditions.CheckNotNull(batchContextFactory);
49             this.requestCallContextFactory = GrpcPreconditions.CheckNotNull(requestCallContextFactory);
50         }
51 
Register(IntPtr key, IOpCompletionCallback callback)52         public void Register(IntPtr key, IOpCompletionCallback callback)
53         {
54             environment.DebugStats.PendingBatchCompletions.Increment();
55 
56             bool lockTaken = false;
57             try
58             {
59                 spinLock.Enter(ref lockTaken);
60 
61                 dict.Add(key, callback);
62                 this.lastRegisteredKey = key;
63             }
64             finally
65             {
66                 if (lockTaken) spinLock.Exit();
67             }
68         }
69 
RegisterBatchCompletion(BatchCompletionDelegate callback, object state)70         public BatchContextSafeHandle RegisterBatchCompletion(BatchCompletionDelegate callback, object state)
71         {
72             var ctx = batchContextFactory();
73             ctx.SetCompletionCallback(callback, state);
74             Register(ctx.Handle, ctx);
75             return ctx;
76         }
77 
RegisterRequestCallCompletion(RequestCallCompletionDelegate callback)78         public RequestCallContextSafeHandle RegisterRequestCallCompletion(RequestCallCompletionDelegate callback)
79         {
80             var ctx = requestCallContextFactory();
81             ctx.CompletionCallback = callback;
82             Register(ctx.Handle, ctx);
83             return ctx;
84         }
85 
Extract(IntPtr key)86         public IOpCompletionCallback Extract(IntPtr key)
87         {
88             IOpCompletionCallback value = null;
89             bool lockTaken = false;
90             try
91             {
92                 spinLock.Enter(ref lockTaken);
93 
94                 value = dict[key];
95                 dict.Remove(key);
96             }
97             finally
98             {
99                 if (lockTaken) spinLock.Exit();
100             }
101             environment.DebugStats.PendingBatchCompletions.Decrement();
102             return value;
103         }
104 
105         /// <summary>
106         /// For testing purposes only. NOT threadsafe.
107         /// </summary>
108         public IntPtr LastRegisteredKey
109         {
110             get { return this.lastRegisteredKey; }
111         }
112 
113         /// <summary>
114         /// IntPtr doesn't implement <c>IEquatable{IntPtr}</c> so we need to use custom comparer to avoid boxing.
115         /// </summary>
116         private class IntPtrComparer : IEqualityComparer<IntPtr>
117         {
Equals(IntPtr x, IntPtr y)118             public bool Equals(IntPtr x, IntPtr y)
119             {
120                 return x == y;
121             }
122 
GetHashCode(IntPtr obj)123             public int GetHashCode(IntPtr obj)
124             {
125                 return obj.GetHashCode();
126             }
127         }
128     }
129 }
130