< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.LifetimeManager
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Channels/LifetimeManager.cs
Line coverage
68%
Covered lines: 64
Uncovered lines: 30
Coverable lines: 94
Total lines: 330
Line coverage: 68%
Branch coverage
50%
Covered branches: 22
Total branches: 44
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
Abort()50%4488.88%
ThrowIfNotOpened()100%22100%
CloseAsync()100%11100%
OnCloseAsync()50%4450%
CloseCoreAsync()25%121245%
AbortCore(...)37.5%8843.75%
DecrementBusyCount()71.42%141463.15%
IncrementBusyCount()100%11100%
IncrementBusyCountWithoutLock()100%11100%
OnAbort()100%11100%
OnEmpty()100%11100%

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
 374724        public LifetimeManager(object mutex)
 25        {
 374726            ThisLock = mutex;
 374727            State = LifetimeState.Opened;
 374728        }
 29
 2482730        public int BusyCount { get; private set; }
 31
 2070032        protected LifetimeState State { get; private set; }
 33
 2251534        protected object ThisLock { get; }
 35
 36        public void Abort()
 37        {
 5838            lock (ThisLock)
 39            {
 5840                if (State == LifetimeState.Closed || _aborted)
 41                {
 042                    return;
 43                }
 44
 5845                _aborted = true;
 5846                State = LifetimeState.Closing;
 5847            }
 48
 5849            OnAbort();
 5850            State = LifetimeState.Closed;
 5851        }
 52
 53        private void ThrowIfNotOpened()
 54        {
 233255            if (!_aborted && State != LifetimeState.Opened)
 56            {
 57            }
 233258        }
 59
 60        public async Task CloseAsync(CancellationToken token)
 61        {
 233262            token.ThrowIfCancellationRequested();
 233263            lock (ThisLock)
 64            {
 233265                ThrowIfNotOpened();
 233266                State = LifetimeState.Closing;
 233267            }
 68
 233269            await OnCloseAsync(token);
 233270            State = LifetimeState.Closed;
 233271        }
 72
 73        protected virtual async Task OnCloseAsync(CancellationToken token)
 74        {
 233275            switch (await CloseCoreAsync(false, token))
 76            {
 77                case CommunicationWaitResult.Expired:
 78                    // TODO: Derive CancellationToken so that the original timeout can be stored inside
 079                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.Format(SR.SFxClose
 80                case CommunicationWaitResult.Aborted:
 081                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(GetType().ToSt
 82            }
 233283        }
 84
 85        public async Task<CommunicationWaitResult> CloseCoreAsync(bool aborting, CancellationToken token)
 86        {
 233287            token.ThrowIfCancellationRequested();
 233288            ICommunicationWaiter busyWaiter = null;
 233289            CommunicationWaitResult result = CommunicationWaitResult.Succeeded;
 90
 233291            lock (ThisLock)
 92            {
 233293                if (BusyCount > 0)
 94                {
 095                    if (_busyWaiter != null)
 96                    {
 097                        if (!aborting && _aborted)
 98                        {
 099                            return CommunicationWaitResult.Aborted;
 100                        }
 101
 0102                        busyWaiter = _busyWaiter;
 103                    }
 104                    else
 105                    {
 0106                        busyWaiter = new AsyncCommunicationWaiter(ThisLock);
 0107                        _busyWaiter = busyWaiter;
 108                    }
 0109                    Interlocked.Increment(ref _busyWaiterCount);
 110                }
 2332111            }
 112
 2332113            if (busyWaiter != null)
 114            {
 0115                result = await busyWaiter.WaitAsync(aborting, token);
 0116                if (Interlocked.Decrement(ref _busyWaiterCount) == 0)
 117                {
 0118                    busyWaiter.Dispose();
 0119                    _busyWaiter = null;
 120                }
 121            }
 122
 2332123            return result;
 2332124        }
 125
 126        private CommunicationWaitResult AbortCore(CancellationToken token)
 127        {
 58128            ICommunicationWaiter busyWaiter = null;
 58129            CommunicationWaitResult result = CommunicationWaitResult.Succeeded;
 130
 58131            lock (ThisLock)
 132            {
 58133                if (BusyCount > 0)
 134                {
 0135                    if (_busyWaiter != null)
 136                    {
 0137                        busyWaiter = _busyWaiter;
 138                    }
 139                    else
 140                    {
 0141                        busyWaiter = new AsyncCommunicationWaiter(ThisLock);
 0142                        _busyWaiter = busyWaiter;
 143                    }
 0144                    Interlocked.Increment(ref _busyWaiterCount);
 145                }
 58146            }
 147
 58148            if (busyWaiter != null)
 149            {
 0150                result = busyWaiter.Wait(true, token);
 0151                if (Interlocked.Decrement(ref _busyWaiterCount) == 0)
 152                {
 0153                    busyWaiter.Dispose();
 0154                    _busyWaiter = null;
 155                }
 156            }
 157
 58158            return result;
 159        }
 160
 161        protected void DecrementBusyCount()
 162        {
 2602163            ICommunicationWaiter busyWaiter = null;
 2602164            bool empty = false;
 165
 2602166            lock (ThisLock)
 167            {
 2602168                if (BusyCount <= 0)
 169                {
 0170                    throw Fx.AssertAndThrow("LifetimeManager.DecrementBusyCount: (this.busyCount > 0)");
 171                }
 2602172                if (--BusyCount == 0)
 173                {
 2251174                    if (_busyWaiter != null)
 175                    {
 0176                        busyWaiter = _busyWaiter;
 0177                        Interlocked.Increment(ref _busyWaiterCount);
 178                    }
 2251179                    empty = true;
 180                }
 2602181            }
 182
 2602183            if (busyWaiter != null)
 184            {
 0185                busyWaiter.Signal();
 0186                if (Interlocked.Decrement(ref _busyWaiterCount) == 0)
 187                {
 0188                    busyWaiter.Dispose();
 0189                    _busyWaiter = null;
 190                }
 191            }
 192
 2602193            if (empty && State == LifetimeState.Opened)
 194            {
 2251195                OnEmpty();
 196            }
 2602197        }
 198
 199        protected virtual void IncrementBusyCount()
 200        {
 2406201            lock (ThisLock)
 202            {
 203                Fx.Assert(State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCount: (this.State == LifetimeSta
 2406204                BusyCount++;
 2406205            }
 2406206        }
 207
 208        protected virtual void IncrementBusyCountWithoutLock()
 209        {
 210            Fx.Assert(State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCountWithoutLock: (this.State == Life
 2476211            BusyCount++;
 2476212        }
 213
 214        protected virtual void OnAbort()
 215        {
 216            // We have decided not to make this configurable
 58217            AbortCore(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token);
 58218        }
 219
 220        protected virtual void OnEmpty()
 221        {
 2172222        }
 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
 247        internal AsyncCommunicationWaiter(object mutex)
 248        {
 249            ThisLock = mutex;
 250            _tcs = new TaskCompletionSource<bool>();
 251        }
 252
 253        private object ThisLock { get; }
 254
 255        public void Dispose()
 256        {
 257            lock (ThisLock)
 258            {
 259                if (_closed)
 260                {
 261                    return;
 262                }
 263
 264                _closed = true;
 265                _tcs?.TrySetResult(false);
 266            }
 267        }
 268
 269        public void Signal()
 270        {
 271            lock (ThisLock)
 272            {
 273                if (_closed)
 274                {
 275                    return;
 276                }
 277
 278                _tcs.TrySetResult(true);
 279            }
 280        }
 281
 282        public async Task<CommunicationWaitResult> WaitAsync(bool aborting, CancellationToken token)
 283        {
 284            Fx.Assert(token.CanBeCanceled, "CancellationToken must be cancellable");
 285
 286            if (_closed)
 287            {
 288                return CommunicationWaitResult.Aborted;
 289            }
 290
 291            if (token.IsCancellationRequested)
 292            {
 293                return CommunicationWaitResult.Expired;
 294            }
 295
 296            if (aborting)
 297            {
 298                _result = CommunicationWaitResult.Aborted;
 299            }
 300
 301            _tcs = new TaskCompletionSource<bool>();
 302            using (token.Register(WaiterTimeout, _tcs))
 303            {
 304                await _tcs.Task;
 305                bool expired = token.IsCancellationRequested;
 306
 307                lock (ThisLock)
 308                {
 309                    if (_result == CommunicationWaitResult.Waiting)
 310                    {
 311                        _result = (expired ? CommunicationWaitResult.Expired : CommunicationWaitResult.Succeeded);
 312                    }
 313                }
 314
 315                return _result;
 316            }
 317        }
 318
 319        public CommunicationWaitResult Wait(bool aborting, CancellationToken token)
 320        {
 321            return WaitAsync(aborting, token).GetAwaiter().GetResult();
 322        }
 323
 324        internal static void WaiterTimeout(object state)
 325        {
 326            var tcs = state as TaskCompletionSource<bool>;
 327            tcs?.TrySetResult(false);
 328        }
 329    }
 330}