< Summary

Class:Microsoft.Azure.ServiceBus.Primitives.ConcurrentExpiringSet`1
Assembly:Microsoft.Azure.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Primitives\ConcurrentExpiringSet.cs
Covered lines:40
Uncovered lines:1
Coverable lines:41
Total lines:100
Line coverage:97.5% (40 of 41)
Covered branches:11
Total branches:14
Branch coverage:78.5% (11 of 14)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor()-100%100%
.cctor()-100%100%
AddOrUpdate(...)-100%100%
Contains(...)-100%50%
Close()-100%100%
CollectExpiredEntriesAsync()-93.75%75%
ThrowIfClosed()-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Primitives\ConcurrentExpiringSet.cs

#LineLine coverage
 1// Copyright (c) Microsoft. All rights reserved.
 2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 3
 4namespace Microsoft.Azure.ServiceBus.Primitives
 5{
 6    using System;
 7    using System.Collections.Generic;
 8    using System.Collections.Concurrent;
 9    using System.Threading;
 10    using System.Threading.Tasks;
 11
 12    sealed class ConcurrentExpiringSet<TKey>
 13    {
 14        readonly ConcurrentDictionary<TKey, DateTime> dictionary;
 15        readonly ICollection<KeyValuePair<TKey, DateTime>> dictionaryAsCollection;
 1016        readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
 1017        volatile TaskCompletionSource<bool> cleanupTaskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOpt
 18        int closeSignaled;
 19        bool closed;
 220        static readonly TimeSpan delayBetweenCleanups = TimeSpan.FromSeconds(30);
 21
 1022        public ConcurrentExpiringSet()
 23        {
 1024            this.dictionary = new ConcurrentDictionary<TKey, DateTime>();
 1025            this.dictionaryAsCollection = dictionary;
 1026            _ = CollectExpiredEntriesAsync(tokenSource.Token);
 1027        }
 28
 29        public void AddOrUpdate(TKey key, DateTime expiration)
 30        {
 1031            this.ThrowIfClosed();
 32
 833            this.dictionary[key] = expiration;
 834            this.cleanupTaskCompletionSource.TrySetResult(true);
 835        }
 36
 37        public bool Contains(TKey key)
 38        {
 639            this.ThrowIfClosed();
 40
 441            return this.dictionary.TryGetValue(key, out var expiration) && expiration > DateTime.UtcNow;
 42        }
 43
 44        public void Close()
 45        {
 1246            if (Interlocked.Exchange(ref this.closeSignaled, 1) != 0)
 47            {
 648                return;
 49            }
 50
 651            this.closed = true;
 52
 653            this.tokenSource.Cancel();
 654            this.cleanupTaskCompletionSource.TrySetCanceled();
 655            this.dictionary.Clear();
 656            this.tokenSource.Dispose();
 657        }
 58
 59        async Task CollectExpiredEntriesAsync(CancellationToken token)
 60        {
 1461            while (!token.IsCancellationRequested)
 62            {
 63                try
 64                {
 1465                    await this.cleanupTaskCompletionSource.Task.ConfigureAwait(false);
 1266                    await Task.Delay(delayBetweenCleanups, token).ConfigureAwait(false);
 467                }
 668                catch (OperationCanceledException)
 69                {
 670                    return;
 71                }
 72
 473                var isEmpty = true;
 474                var utcNow = DateTime.UtcNow;
 1675                foreach (var kvp in this.dictionary)
 76                {
 477                    isEmpty = false;
 478                    var expiration = kvp.Value;
 479                    if (utcNow > expiration)
 80                    {
 481                        this.dictionaryAsCollection.Remove(kvp);
 82                    }
 83                }
 84
 485                if (isEmpty)
 86                {
 087                    this.cleanupTaskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuatio
 88                }
 89            }
 690        }
 91
 92        void ThrowIfClosed()
 93        {
 1694            if (closed)
 95            {
 496                throw new ObjectDisposedException($"ConcurrentExpiringSet has already been closed. Please create a new s
 97            }
 1298        }
 99    }
 100}