< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Dispatcher.ConcurrencyInstanceContextFacet
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ConcurrencyBehavior.cs
Line coverage
9%
Covered lines: 2
Uncovered lines: 19
Coverable lines: 21
Total lines: 302
Line coverage: 9.5%
Branch coverage
18%
Covered branches: 3
Total branches: 16
Branch coverage: 18.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
DequeueFrom(...)0%220%
DequeueWaiter()0%440%
EnqueueNewMessage()0%220%
EnqueueCalloutMessage()0%220%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/ConcurrencyBehavior.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using CoreWCF.Runtime;
 9
 10namespace CoreWCF.Dispatcher
 11{
 12    internal class ConcurrencyBehavior
 13    {
 14        private readonly ConcurrencyMode _concurrencyMode;
 15        private readonly bool _enforceOrderedReceive;
 16
 17        internal ConcurrencyBehavior(DispatchRuntime runtime)
 18        {
 19            _concurrencyMode = runtime.ConcurrencyMode;
 20            _enforceOrderedReceive = runtime.EnsureOrderedDispatch;
 21            //this.supportsTransactedBatch = ConcurrencyBehavior.SupportsTransactedBatch(runtime.ChannelDispatcher);
 22        }
 23
 24        internal bool IsConcurrent(MessageRpc rpc)
 25        {
 26            return IsConcurrent(_concurrencyMode, _enforceOrderedReceive, rpc.Channel.HasSession/*, this.supportsTransac
 27        }
 28
 29        internal static bool IsConcurrent(ConcurrencyMode concurrencyMode, bool ensureOrderedDispatch, bool hasSession /
 30        {
 31            //if (supportsTransactedBatch)
 32            //{
 33            //    return false;
 34            //}
 35
 36            if (concurrencyMode != ConcurrencyMode.Single)
 37            {
 38                return true;
 39            }
 40
 41            if (hasSession)
 42            {
 43                return false;
 44            }
 45
 46            if (ensureOrderedDispatch)
 47            {
 48                return false;
 49            }
 50
 51            return true;
 52        }
 53
 54        internal static bool IsConcurrent(ChannelDispatcher runtime, bool hasSession)
 55        {
 56            bool isConcurrencyModeSingle = true;
 57
 58            //if (ConcurrencyBehavior.SupportsTransactedBatch(runtime))
 59            //{
 60            //    return false;
 61            //}
 62
 63            foreach (EndpointDispatcher endpointDispatcher in runtime.Endpoints)
 64            {
 65                if (endpointDispatcher.DispatchRuntime.EnsureOrderedDispatch)
 66                {
 67                    return false;
 68                }
 69
 70                if (endpointDispatcher.DispatchRuntime.ConcurrencyMode != ConcurrencyMode.Single)
 71                {
 72                    isConcurrencyModeSingle = false;
 73                }
 74            }
 75
 76            if (!isConcurrencyModeSingle)
 77            {
 78                return true;
 79            }
 80
 81            if (!hasSession)
 82            {
 83                return true;
 84            }
 85
 86            return false;
 87        }
 88
 89        internal async Task LockInstanceAsync(MessageRpc rpc)
 90        {
 91            if (_concurrencyMode != ConcurrencyMode.Multiple)
 92            {
 93                ConcurrencyInstanceContextFacet resource = rpc.InstanceContext.Concurrency;
 94                Task waiter = null;
 95                lock (rpc.InstanceContext.ThisLock)
 96                {
 97                    if (!resource.Locked)
 98                    {
 99                        resource.Locked = true;
 100                    }
 101                    else
 102                    {
 103                        waiter = resource.EnqueueNewMessage();
 104                    }
 105                }
 106
 107                if (waiter != null)
 108                {
 109                    await waiter;
 110                }
 111
 112                // TODO: Throw this on setup
 113                if (_concurrencyMode == ConcurrencyMode.Reentrant)
 114                {
 115                    throw new NotSupportedException(nameof(ConcurrencyMode.Reentrant));
 116                }
 117            }
 118        }
 119
 120        internal void UnlockInstance(ref MessageRpc rpc)
 121        {
 122            if (_concurrencyMode != ConcurrencyMode.Multiple)
 123            {
 124                UnlockInstance(rpc.InstanceContext);
 125            }
 126        }
 127
 128        internal static void UnlockInstanceBeforeCallout(OperationContext operationContext)
 129        {
 130            if (operationContext != null && operationContext.IsServiceReentrant)
 131            {
 132                UnlockInstance(operationContext.InstanceContext);
 133            }
 134        }
 135
 136        private static void UnlockInstance(InstanceContext instanceContext)
 137        {
 138            ConcurrencyInstanceContextFacet resource = instanceContext.Concurrency;
 139
 140            lock (instanceContext.ThisLock)
 141            {
 142                if (resource.HasWaiters)
 143                {
 144                    resource.DequeueWaiter();
 145                }
 146                else
 147                {
 148                    //We have no pending Callouts and no new Messages to process
 149                    resource.Locked = false;
 150                }
 151            }
 152        }
 153
 154        // TODO: Make async to remove blocking Wait call
 155        internal static Task LockInstanceAfterCalloutAsync(OperationContext operationContext)
 156        {
 157            if (operationContext != null)
 158            {
 159                InstanceContext instanceContext = operationContext.InstanceContext;
 160
 161                if (operationContext.IsServiceReentrant)
 162                {
 163                    ConcurrencyInstanceContextFacet resource = instanceContext.Concurrency;
 164                    bool needToWait = false;
 165                    lock (instanceContext.ThisLock)
 166                    {
 167                        if (!resource.Locked)
 168                        {
 169                            resource.Locked = true;
 170                        }
 171                    }
 172
 173                    if (needToWait)
 174                    {
 175                        return resource.EnqueueCalloutMessage();
 176                    }
 177                }
 178            }
 179
 180            return Task.CompletedTask;
 181        }
 182
 183        internal interface IWaiter
 184        {
 185            void Signal();
 186        }
 187
 188        private class MessageRpcWaiter : IWaiter
 189        {
 190            private readonly IResumeMessageRpc _resume;
 191
 192            internal MessageRpcWaiter(IResumeMessageRpc resume)
 193            {
 194                _resume = resume;
 195            }
 196
 197            void IWaiter.Signal()
 198            {
 199                try
 200                {
 201                    _resume.Resume(out bool alreadyResumedNoLock);
 202
 203                    if (alreadyResumedNoLock)
 204                    {
 205                        Fx.Assert("ConcurrencyBehavior resumed more than once for same call.");
 206                    }
 207                }
 208                catch (Exception e)
 209                {
 210                    if (Fx.IsFatal(e))
 211                    {
 212                        throw;
 213                    }
 214                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
 215                }
 216            }
 217        }
 218
 219        private class ThreadWaiter : IWaiter
 220        {
 221            private readonly ManualResetEvent _wait = new ManualResetEvent(false);
 222
 223            void IWaiter.Signal()
 224            {
 225                _wait.Set();
 226            }
 227
 228            internal void Wait()
 229            {
 230                _wait.WaitOne();
 231                _wait.Dispose();
 232            }
 233        }
 234    }
 235
 236    internal class ConcurrencyInstanceContextFacet
 237    {
 238        internal bool Locked;
 239        private Queue<TaskCompletionSource<object>> _calloutMessageQueue;
 240        private Queue<TaskCompletionSource<object>> _newMessageQueue;
 241
 242        internal bool HasWaiters
 243        {
 244            get
 245            {
 2327246                return (((_calloutMessageQueue != null) && (_calloutMessageQueue.Count > 0)) ||
 2327247                        ((_newMessageQueue != null) && (_newMessageQueue.Count > 0)));
 248            }
 249        }
 250
 251        private TaskCompletionSource<object> DequeueFrom(Queue<TaskCompletionSource<object>> queue)
 252        {
 0253            TaskCompletionSource<object> waiter = queue.Dequeue();
 254
 0255            if (queue.Count == 0)
 256            {
 0257                queue.TrimExcess();
 258            }
 259
 0260            return waiter;
 261        }
 262
 263        internal void DequeueWaiter()
 264        {
 265            TaskCompletionSource<object> waiter;
 0266            if ((_calloutMessageQueue != null) && (_calloutMessageQueue.Count > 0))
 267            {
 0268                waiter = DequeueFrom(_calloutMessageQueue);
 269            }
 270            else
 271            {
 0272                waiter = DequeueFrom(_newMessageQueue);
 273            }
 274
 0275            waiter.TrySetResult(null);
 0276        }
 277
 278        internal Task EnqueueNewMessage()
 279        {
 0280            if (_newMessageQueue == null)
 281            {
 0282                _newMessageQueue = new Queue<TaskCompletionSource<object>>();
 283            }
 284            // Prevent release of waiter from running the waiter on the releasing thread by using RunContinuationsAsynch
 0285            var waiter = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
 0286            _newMessageQueue.Enqueue(waiter);
 0287            return waiter.Task;
 288        }
 289
 290        internal Task EnqueueCalloutMessage()
 291        {
 0292            if (_calloutMessageQueue == null)
 293            {
 0294                _calloutMessageQueue = new Queue<TaskCompletionSource<object>>();
 295            }
 296            // Prevent release of waiter from running the waiter on the releasing thread by using RunContinuationsAsynch
 0297            var waiter = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
 0298            _calloutMessageQueue.Enqueue(waiter);
 0299            return waiter.Task;
 300        }
 301    }
 302}