< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Runtime.AsyncLock
Assembly: CoreWCF.Http
File(s): /home/runner/work/CoreWCF/CoreWCF/src/Common/src/CoreWCF/Runtime/AsyncLock.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 58
Coverable lines: 58
Total lines: 151
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
.ctor()100%110%
TakeLockAsync()0%440%
TakeLockCoreAsync()100%110%
TakeLock()0%440%
DisposeAsync()0%220%
.ctor(...)100%110%
DisposeAsync()0%220%
DisposeCoreAsync()100%110%
Dispose()0%220%
Create()100%110%
Return(...)0%220%

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    {
 014        private static readonly ObjectPool<SemaphoreSlim> s_semaphorePool = (new DefaultObjectPoolProvider { MaximumReta
 015            .Create(new SemaphoreSlimPooledObjectPolicy());
 16
 17        private AsyncLocal<SemaphoreSlim> _currentSemaphore;
 18        private SemaphoreSlim _topLevelSemaphore;
 19        private bool _isDisposed;
 20
 021        public AsyncLock()
 22        {
 023            _topLevelSemaphore = s_semaphorePool.Get();
 024            _currentSemaphore = new AsyncLocal<SemaphoreSlim>();
 025        }
 26
 27        public Task<IAsyncDisposable> TakeLockAsync()
 28        {
 029            if (_isDisposed)
 030                throw new ObjectDisposedException(nameof(AsyncLock));
 31
 032            _currentSemaphore.Value = _currentSemaphore.Value ?? _topLevelSemaphore;
 033            SemaphoreSlim currentSem = _currentSemaphore.Value;
 034            var nextSem = s_semaphorePool.Get();
 035            _currentSemaphore.Value = nextSem;
 036            var safeRelease = new SafeSemaphoreRelease(currentSem, nextSem, this);
 037            return TakeLockCoreAsync(currentSem, safeRelease);
 38        }
 39
 40        private async Task<IAsyncDisposable> TakeLockCoreAsync(SemaphoreSlim currentSemaphore, SafeSemaphoreRelease safe
 41        {
 042            await currentSemaphore.WaitAsync();
 043            return safeSemaphoreRelease;
 044        }
 45
 46        public IDisposable TakeLock()
 47        {
 048            if (_isDisposed)
 049                throw new ObjectDisposedException(nameof(AsyncLock));
 50
 051            _currentSemaphore.Value = _currentSemaphore.Value ?? _topLevelSemaphore;
 052            SemaphoreSlim currentSem = _currentSemaphore.Value;
 053            currentSem.Wait();
 054            var nextSem = s_semaphorePool.Get();
 055            _currentSemaphore.Value = nextSem;
 056            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            {
 081                _currentSemaphore = currentSemaphore;
 082                _nextSemaphore = nextSemaphore;
 083                _asyncLock = asyncLock;
 084            }
 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.
 093                if (_currentSemaphore == _asyncLock._topLevelSemaphore)
 94                {
 095                    _asyncLock._currentSemaphore.Value = null;
 96                }
 97                else
 98                {
 099                    _asyncLock._currentSemaphore.Value = _currentSemaphore;
 100                }
 101
 0102                return DisposeCoreAsync();
 103            }
 104
 105            private async ValueTask DisposeCoreAsync()
 106            {
 0107                await _nextSemaphore.WaitAsync();
 0108                _currentSemaphore.Release();
 0109                _nextSemaphore.Release();
 0110                s_semaphorePool.Return(_nextSemaphore);
 0111            }
 112
 113            public void Dispose()
 114            {
 115                Debug.Assert(_nextSemaphore == _asyncLock._currentSemaphore.Value, "_nextSemaphore was expected to by th
 0116                if (_currentSemaphore == _asyncLock._topLevelSemaphore)
 117                {
 0118                    _asyncLock._currentSemaphore.Value = null;
 119                }
 120                else
 121                {
 0122                    _asyncLock._currentSemaphore.Value = _currentSemaphore;
 123                }
 124
 0125                _nextSemaphore.Wait();
 0126                _currentSemaphore.Release();
 0127                _nextSemaphore.Release();
 0128                s_semaphorePool.Return(_nextSemaphore);
 0129            }
 130        }
 131
 132        private class SemaphoreSlimPooledObjectPolicy : PooledObjectPolicy<SemaphoreSlim>
 133        {
 134            public override SemaphoreSlim Create()
 135            {
 0136                return new SemaphoreSlim(1);
 137            }
 138
 139            public override bool Return(SemaphoreSlim obj)
 140            {
 0141                if (obj.CurrentCount != 1)
 142                {
 143                    Debug.Fail("Shouldn't be returning semaphore with a count != 1");
 0144                    return false;
 145                }
 146
 0147                return true;
 148            }
 149        }
 150    }
 151}