< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Runtime.AsyncLock
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/Common/src/CoreWCF/Runtime/AsyncLock.cs
Line coverage
77%
Covered lines: 45
Uncovered lines: 13
Coverable lines: 58
Total lines: 151
Line coverage: 77.5%
Branch coverage
56%
Covered branches: 9
Total branches: 16
Branch coverage: 56.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor()100%11100%
TakeLockAsync()75%4487.5%
TakeLockCoreAsync()100%11100%
TakeLock()75%4487.5%
DisposeAsync()0%220%
.ctor(...)100%11100%
DisposeAsync()50%2275%
DisposeCoreAsync()100%11100%
Dispose()50%2287.5%
Create()100%11100%
Return(...)50%2266.66%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/Common/src/CoreWCF/Runtime/AsyncLock.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.Diagnostics;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Microsoft.Extensions.ObjectPool;
 9
 10namespace CoreWCF.Runtime
 11{
 12    internal class AsyncLock : IAsyncDisposable
 13    {
 1014        private static readonly ObjectPool<SemaphoreSlim> s_semaphorePool = (new DefaultObjectPoolProvider { MaximumReta
 1015            .Create(new SemaphoreSlimPooledObjectPolicy());
 16
 17        private AsyncLocal<SemaphoreSlim> _currentSemaphore;
 18        private SemaphoreSlim _topLevelSemaphore;
 19        private bool _isDisposed;
 20
 305821        public AsyncLock()
 22        {
 305823            _topLevelSemaphore = s_semaphorePool.Get();
 305824            _currentSemaphore = new AsyncLocal<SemaphoreSlim>();
 305825        }
 26
 27        public Task<IAsyncDisposable> TakeLockAsync()
 28        {
 53129            if (_isDisposed)
 030                throw new ObjectDisposedException(nameof(AsyncLock));
 31
 53132            _currentSemaphore.Value = _currentSemaphore.Value ?? _topLevelSemaphore;
 53133            SemaphoreSlim currentSem = _currentSemaphore.Value;
 53134            var nextSem = s_semaphorePool.Get();
 53135            _currentSemaphore.Value = nextSem;
 53136            var safeRelease = new SafeSemaphoreRelease(currentSem, nextSem, this);
 53137            return TakeLockCoreAsync(currentSem, safeRelease);
 38        }
 39
 40        private async Task<IAsyncDisposable> TakeLockCoreAsync(SemaphoreSlim currentSemaphore, SafeSemaphoreRelease safe
 41        {
 53142            await currentSemaphore.WaitAsync();
 53143            return safeSemaphoreRelease;
 53144        }
 45
 46        public IDisposable TakeLock()
 47        {
 7248            if (_isDisposed)
 049                throw new ObjectDisposedException(nameof(AsyncLock));
 50
 7251            _currentSemaphore.Value = _currentSemaphore.Value ?? _topLevelSemaphore;
 7252            SemaphoreSlim currentSem = _currentSemaphore.Value;
 7253            currentSem.Wait();
 7254            var nextSem = s_semaphorePool.Get();
 7255            _currentSemaphore.Value = nextSem;
 7256            return new SafeSemaphoreRelease(currentSem, nextSem, this);
 57        }
 58
 59        public async ValueTask DisposeAsync()
 60        {
 061            if (_isDisposed)
 062                return;
 63
 064            _isDisposed = true;
 65            // Ensure the lock isn't held. If it is, wait for it to be released
 66            // before completing the dispose.
 067            await _topLevelSemaphore.WaitAsync();
 068            _topLevelSemaphore.Release();
 069            s_semaphorePool.Return(_topLevelSemaphore);
 070            _topLevelSemaphore = null;
 071        }
 72
 73        private struct SafeSemaphoreRelease : IAsyncDisposable, IDisposable
 74        {
 75            private SemaphoreSlim _currentSemaphore;
 76            private SemaphoreSlim _nextSemaphore;
 77            private AsyncLock _asyncLock;
 78
 79            public SafeSemaphoreRelease(SemaphoreSlim currentSemaphore, SemaphoreSlim nextSemaphore, AsyncLock asyncLock
 80            {
 60381                _currentSemaphore = currentSemaphore;
 60382                _nextSemaphore = nextSemaphore;
 60383                _asyncLock = asyncLock;
 60384            }
 85
 86            public ValueTask DisposeAsync()
 87            {
 88                Debug.Assert(_nextSemaphore == _asyncLock._currentSemaphore.Value, "_nextSemaphore was expected to by th
 89                // Update _asyncLock._currentSemaphore in the calling ExecutionContext
 90                // and defer any awaits to DisposeCoreAsync(). If this isn't done, the
 91                // update will happen in a copy of the ExecutionContext and the caller
 92                // won't see the changes.
 53193                if (_currentSemaphore == _asyncLock._topLevelSemaphore)
 94                {
 53195                    _asyncLock._currentSemaphore.Value = null;
 96                }
 97                else
 98                {
 099                    _asyncLock._currentSemaphore.Value = _currentSemaphore;
 100                }
 101
 531102                return DisposeCoreAsync();
 103            }
 104
 105            private async ValueTask DisposeCoreAsync()
 106            {
 531107                await _nextSemaphore.WaitAsync();
 531108                _currentSemaphore.Release();
 531109                _nextSemaphore.Release();
 531110                s_semaphorePool.Return(_nextSemaphore);
 531111            }
 112
 113            public void Dispose()
 114            {
 115                Debug.Assert(_nextSemaphore == _asyncLock._currentSemaphore.Value, "_nextSemaphore was expected to by th
 72116                if (_currentSemaphore == _asyncLock._topLevelSemaphore)
 117                {
 72118                    _asyncLock._currentSemaphore.Value = null;
 119                }
 120                else
 121                {
 0122                    _asyncLock._currentSemaphore.Value = _currentSemaphore;
 123                }
 124
 72125                _nextSemaphore.Wait();
 72126                _currentSemaphore.Release();
 72127                _nextSemaphore.Release();
 72128                s_semaphorePool.Return(_nextSemaphore);
 72129            }
 130        }
 131
 132        private class SemaphoreSlimPooledObjectPolicy : PooledObjectPolicy<SemaphoreSlim>
 133        {
 134            public override SemaphoreSlim Create()
 135            {
 3064136                return new SemaphoreSlim(1);
 137            }
 138
 139            public override bool Return(SemaphoreSlim obj)
 140            {
 603141                if (obj.CurrentCount != 1)
 142                {
 143                    Debug.Fail("Shouldn't be returning semaphore with a count != 1");
 0144                    return false;
 145                }
 146
 603147                return true;
 148            }
 149        }
 150    }
 151}