|  | @@ -36,7 +36,8 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |          static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<CompletionRegistry>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          readonly GrpcEnvironment environment;
 | 
	
		
			
				|  |  | -        readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>(new IntPtrComparer());
 | 
	
		
			
				|  |  | +        readonly Dictionary<IntPtr, OpCompletionDelegate> dict = new Dictionary<IntPtr, OpCompletionDelegate>(new IntPtrComparer());
 | 
	
		
			
				|  |  | +        readonly object myLock = new object();
 | 
	
		
			
				|  |  |          IntPtr lastRegisteredKey;  // only for testing
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public CompletionRegistry(GrpcEnvironment environment)
 | 
	
	
		
			
				|  | @@ -47,32 +48,41 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |          public void Register(IntPtr key, OpCompletionDelegate callback)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              environment.DebugStats.PendingBatchCompletions.Increment();
 | 
	
		
			
				|  |  | -            GrpcPreconditions.CheckState(dict.TryAdd(key, callback));
 | 
	
		
			
				|  |  | -            this.lastRegisteredKey = key;
 | 
	
		
			
				|  |  | +            lock (myLock)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                dict.Add(key, callback);
 | 
	
		
			
				|  |  | +                this.lastRegisteredKey = key;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            // TODO(jtattermusch): get rid of new delegate creation here
 | 
	
		
			
				|  |  |              OpCompletionDelegate opCallback = ((success) => HandleBatchCompletion(success, ctx, callback));
 | 
	
		
			
				|  |  |              Register(ctx.Handle, opCallback);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public void RegisterRequestCallCompletion(RequestCallContextSafeHandle ctx, RequestCallCompletionDelegate callback)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            // TODO(jtattermusch): get rid of new delegate creation here
 | 
	
		
			
				|  |  |              OpCompletionDelegate opCallback = ((success) => HandleRequestCallCompletion(success, ctx, callback));
 | 
	
		
			
				|  |  |              Register(ctx.Handle, opCallback);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public OpCompletionDelegate Extract(IntPtr key)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            OpCompletionDelegate value;
 | 
	
		
			
				|  |  | -            GrpcPreconditions.CheckState(dict.TryRemove(key, out value));
 | 
	
		
			
				|  |  | +            OpCompletionDelegate value = null;
 | 
	
		
			
				|  |  | +            lock (myLock)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                value = dict[key];
 | 
	
		
			
				|  |  | +                dict.Remove(key);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |              environment.DebugStats.PendingBatchCompletions.Decrement();
 | 
	
		
			
				|  |  |              return value;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  | -        /// For testing purposes only.
 | 
	
		
			
				|  |  | +        /// For testing purposes only. NOT threadsafe.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          public IntPtr LastRegisteredKey
 | 
	
		
			
				|  |  |          {
 |