< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.AsyncCommunicationWaiter
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 38
Coverable lines: 38
Total lines: 330
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 18
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
Dispose()0%440%
Signal()0%220%
WaitAsync()0%10100%
Wait(...)100%110%
WaiterTimeout(...)0%220%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.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.Threading;
 6using System.Threading.Tasks;
 7using CoreWCF.Runtime;
 8
 9namespace CoreWCF.Channels
 10{
 11    internal enum LifetimeState
 12    {
 13        Opened,
 14        Closing,
 15        Closed
 16    }
 17
 18    internal class LifetimeManager
 19    {
 20        private bool _aborted;
 21        private ICommunicationWaiter _busyWaiter;
 22        private int _busyWaiterCount;
 23
 24        public LifetimeManager(object mutex)
 25        {
 26            ThisLock = mutex;
 27            State = LifetimeState.Opened;
 28        }
 29
 30        public int BusyCount { get; private set; }
 31
 32        protected LifetimeState State { get; private set; }
 33
 34        protected object ThisLock { get; }
 35
 36        public void Abort()
 37        {
 38            lock (ThisLock)
 39            {
 40                if (State == LifetimeState.Closed || _aborted)
 41                {
 42                    return;
 43                }
 44
 45                _aborted = true;
 46                State = LifetimeState.Closing;
 47            }
 48
 49            OnAbort();
 50            State = LifetimeState.Closed;
 51        }
 52
 53        private void ThrowIfNotOpened()
 54        {
 55            if (!_aborted && State != LifetimeState.Opened)
 56            {
 57            }
 58        }
 59
 60        public async Task CloseAsync(CancellationToken token)
 61        {
 62            token.ThrowIfCancellationRequested();
 63            lock (ThisLock)
 64            {
 65                ThrowIfNotOpened();
 66                State = LifetimeState.Closing;
 67            }
 68
 69            await OnCloseAsync(token);
 70            State = LifetimeState.Closed;
 71        }
 72
 73        protected virtual async Task OnCloseAsync(CancellationToken token)
 74        {
 75            switch (await CloseCoreAsync(false, token))
 76            {
 77                case CommunicationWaitResult.Expired:
 78                    // TODO: Derive CancellationToken so that the original timeout can be stored inside
 79                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.Format(SR.SFxClose
 80                case CommunicationWaitResult.Aborted:
 81                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToSt
 82            }
 83        }
 84
 85        public async Task<CommunicationWaitResult> CloseCoreAsync(bool aborting, CancellationToken token)
 86        {
 87            token.ThrowIfCancellationRequested();
 88            ICommunicationWaiter busyWaiter = null;
 89            CommunicationWaitResult result = CommunicationWaitResult.Succeeded;
 90
 91            lock (ThisLock)
 92            {
 93                if (BusyCount > 0)
 94                {
 95                    if (_busyWaiter != null)
 96                    {
 97                        if (!aborting && _aborted)
 98                        {
 99                            return CommunicationWaitResult.Aborted;
 100                        }
 101
 102                        busyWaiter = _busyWaiter;
 103                    }
 104                    else
 105                    {
 106                        busyWaiter = new AsyncCommunicationWaiter(ThisLock);
 107                        _busyWaiter = busyWaiter;
 108                    }
 109                    Interlocked.Increment(ref _busyWaiterCount);
 110                }
 111            }
 112
 113            if (busyWaiter != null)
 114            {
 115                result = await busyWaiter.WaitAsync(aborting, token);
 116                if (Interlocked.Decrement(ref _busyWaiterCount) == 0)
 117                {
 118                    busyWaiter.Dispose();
 119                    _busyWaiter = null;
 120                }
 121            }
 122
 123            return result;
 124        }
 125
 126        private CommunicationWaitResult AbortCore(CancellationToken token)
 127        {
 128            ICommunicationWaiter busyWaiter = null;
 129            CommunicationWaitResult result = CommunicationWaitResult.Succeeded;
 130
 131            lock (ThisLock)
 132            {
 133                if (BusyCount > 0)
 134                {
 135                    if (_busyWaiter != null)
 136                    {
 137                        busyWaiter = _busyWaiter;
 138                    }
 139                    else
 140                    {
 141                        busyWaiter = new AsyncCommunicationWaiter(ThisLock);
 142                        _busyWaiter = busyWaiter;
 143                    }
 144                    Interlocked.Increment(ref _busyWaiterCount);
 145                }
 146            }
 147
 148            if (busyWaiter != null)
 149            {
 150                result = busyWaiter.Wait(true, token);
 151                if (Interlocked.Decrement(ref _busyWaiterCount) == 0)
 152                {
 153                    busyWaiter.Dispose();
 154                    _busyWaiter = null;
 155                }
 156            }
 157
 158            return result;
 159        }
 160
 161        protected void DecrementBusyCount()
 162        {
 163            ICommunicationWaiter busyWaiter = null;
 164            bool empty = false;
 165
 166            lock (ThisLock)
 167            {
 168                if (BusyCount <= 0)
 169                {
 170                    throw Fx.AssertAndThrow("LifetimeManager.DecrementBusyCount: (this.busyCount > 0)");
 171                }
 172                if (--BusyCount == 0)
 173                {
 174                    if (_busyWaiter != null)
 175                    {
 176                        busyWaiter = _busyWaiter;
 177                        Interlocked.Increment(ref _busyWaiterCount);
 178                    }
 179                    empty = true;
 180                }
 181            }
 182
 183            if (busyWaiter != null)
 184            {
 185                busyWaiter.Signal();
 186                if (Interlocked.Decrement(ref _busyWaiterCount) == 0)
 187                {
 188                    busyWaiter.Dispose();
 189                    _busyWaiter = null;
 190                }
 191            }
 192
 193            if (empty && State == LifetimeState.Opened)
 194            {
 195                OnEmpty();
 196            }
 197        }
 198
 199        protected virtual void IncrementBusyCount()
 200        {
 201            lock (ThisLock)
 202            {
 203                Fx.Assert(State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCount: (this.State == LifetimeSta
 204                BusyCount++;
 205            }
 206        }
 207
 208        protected virtual void IncrementBusyCountWithoutLock()
 209        {
 210            Fx.Assert(State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCountWithoutLock: (this.State == Life
 211            BusyCount++;
 212        }
 213
 214        protected virtual void OnAbort()
 215        {
 216            // We have decided not to make this configurable
 217            AbortCore(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token);
 218        }
 219
 220        protected virtual void OnEmpty()
 221        {
 222        }
 223    }
 224
 225    internal enum CommunicationWaitResult
 226    {
 227        Waiting,
 228        Succeeded,
 229        Expired,
 230        Aborted
 231    }
 232
 233    internal interface ICommunicationWaiter : IDisposable
 234    {
 235        void Signal();
 236        Task<CommunicationWaitResult> WaitAsync(bool aborting, CancellationToken token);
 237        CommunicationWaitResult Wait(bool aborting, CancellationToken token);
 238    }
 239
 240    internal class AsyncCommunicationWaiter : ICommunicationWaiter
 241    {
 242        private bool _closed;
 243        private CommunicationWaitResult _result;
 244
 245        private TaskCompletionSource<bool> _tcs;
 246
 0247        internal AsyncCommunicationWaiter(object mutex)
 248        {
 0249            ThisLock = mutex;
 0250            _tcs = new TaskCompletionSource<bool>();
 0251        }
 252
 0253        private object ThisLock { get; }
 254
 255        public void Dispose()
 256        {
 0257            lock (ThisLock)
 258            {
 0259                if (_closed)
 260                {
 0261                    return;
 262                }
 263
 0264                _closed = true;
 0265                _tcs?.TrySetResult(false);
 0266            }
 0267        }
 268
 269        public void Signal()
 270        {
 0271            lock (ThisLock)
 272            {
 0273                if (_closed)
 274                {
 0275                    return;
 276                }
 277
 0278                _tcs.TrySetResult(true);
 0279            }
 0280        }
 281
 282        public async Task<CommunicationWaitResult> WaitAsync(bool aborting, CancellationToken token)
 283        {
 284            Fx.Assert(token.CanBeCanceled, "CancellationToken must be cancellable");
 285
 0286            if (_closed)
 287            {
 0288                return CommunicationWaitResult.Aborted;
 289            }
 290
 0291            if (token.IsCancellationRequested)
 292            {
 0293                return CommunicationWaitResult.Expired;
 294            }
 295
 0296            if (aborting)
 297            {
 0298                _result = CommunicationWaitResult.Aborted;
 299            }
 300
 0301            _tcs = new TaskCompletionSource<bool>();
 0302            using (token.Register(WaiterTimeout, _tcs))
 303            {
 0304                await _tcs.Task;
 0305                bool expired = token.IsCancellationRequested;
 306
 0307                lock (ThisLock)
 308                {
 0309                    if (_result == CommunicationWaitResult.Waiting)
 310                    {
 0311                        _result = (expired ? CommunicationWaitResult.Expired : CommunicationWaitResult.Succeeded);
 312                    }
 0313                }
 314
 0315                return _result;
 316            }
 0317        }
 318
 319        public CommunicationWaitResult Wait(bool aborting, CancellationToken token)
 320        {
 0321            return WaitAsync(aborting, token).GetAwaiter().GetResult();
 322        }
 323
 324        internal static void WaiterTimeout(object state)
 325        {
 0326            var tcs = state as TaskCompletionSource<bool>;
 0327            tcs?.TrySetResult(false);
 0328        }
 329    }
 330}