< Summary

Information
Class: Amusoft.Toolkit.Threading.AmbientScope<T>
Assembly: Amusoft.Toolkit.Threading
File(s): /home/runner/work/Amusoft.Toolkit.Threading/Amusoft.Toolkit.Threading/src/Amusoft.Toolkit.Threading/AmbientScope.cs
Tag: 19_10540409038
Line coverage
100%
Covered lines: 39
Uncovered lines: 0
Coverable lines: 39
Total lines: 120
Line coverage: 100%
Branch coverage
96%
Covered branches: 25
Total branches: 26
Branch coverage: 96.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_Current()100%11100%
.ctor()100%66100%
get_ParentScope()100%11100%
GetParentScopes()90%1010100%
ChangeCurrentScope(...)100%44100%
Dispose()100%11100%
Dispose(...)100%66100%

File(s)

/home/runner/work/Amusoft.Toolkit.Threading/Amusoft.Toolkit.Threading/src/Amusoft.Toolkit.Threading/AmbientScope.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Threading;
 4
 5#if NETSTANDARD2_0
 6using Amusoft.Toolkit.Threading.Compat;
 7#endif
 8
 9namespace Amusoft.Toolkit.Threading;
 10
 11/// <summary>
 12/// Ambient scope utility class
 13/// </summary>
 14/// <typeparam name="T"></typeparam>
 15public abstract class AmbientScope<T> : IDisposable
 16  where T: AmbientScope<T>
 17{
 118  private static readonly AsyncLocal<Stack<T>> GlobalScopes = new()
 119  {
 120    Value = new Stack<T>()
 121  };
 22
 23  // ReSharper disable once InconsistentNaming
 124  private static readonly AsyncLocal<T?> GlobalCurrent = new ();
 25
 26  /// <summary>
 27  /// Current ambient instance
 28  /// </summary>
 1729  public static T? Current => GlobalCurrent.Value;
 30
 1631  private readonly AsyncLocal<T?> _localParentScope = new();
 32
 33  /// <summary>
 34  /// Parent scope of current scope
 35  /// </summary>
 2036  public T? ParentScope => _localParentScope.Value;
 37
 38  /// <summary>
 39  /// Parent scopes of current scope
 40  /// </summary>
 41  public IEnumerable<T> GetParentScopes(Predicate<T>? continueCondition = default)
 42  {
 243    var current = this;
 644    while (current != null)
 45    {
 646      if (current.ParentScope == null || current._localParentScope.Value == null)
 47        break;
 48
 449      if (continueCondition?.Invoke(current._localParentScope.Value) ?? true)
 50      {
 451        yield return current._localParentScope.Value;
 452        current = current._localParentScope.Value;
 53      }
 54    }
 255  }
 56
 57  /// <summary>
 58  /// Constructor
 59  /// </summary>
 1660  protected AmbientScope()
 61  {
 1662    if (GlobalCurrent.Value != null)
 63    {
 664      _localParentScope.Value = GlobalCurrent.Value;
 65    }
 66
 1667    if (GlobalScopes.Value == null)
 968      GlobalScopes.Value = new Stack<T>();
 69
 1670    if (this is T c)
 1671      GlobalScopes.Value.Push(c);
 1672    ChangeCurrentScope(this as T);
 1673  }
 74
 75  private void ChangeCurrentScope(T? scope)
 76  {
 3077    if (GlobalCurrent.Value == null)
 78    {
 1079      if (scope is not null)
 80      {
 1081        GlobalCurrent.Value = scope;
 82      }
 83    }
 84    else
 85    {
 2086      GlobalCurrent.Value = scope;
 87    }
 2088  }
 89
 90  /// <summary>
 91  /// Dispose method
 92  /// </summary>
 93  public void Dispose()
 94  {
 1695    Dispose(true);
 1696    GC.SuppressFinalize(this);
 1697  }
 98
 99  private bool _disposed;
 100
 101  /// <summary>
 102  /// Dispose
 103  /// </summary>
 104  /// <param name="disposing">true if called by dispose, false if it is called by finalizer</param>
 105  protected virtual void Dispose(bool disposing)
 106  {
 16107    if(_disposed)
 2108      return;
 109
 14110    if (disposing)
 111    {
 14112      if (GlobalScopes.Value!.TryPop(out var scope))
 113      {
 14114        ChangeCurrentScope(scope.ParentScope);
 115      }
 116    }
 117
 14118    _disposed = true;
 14119  }
 120}