< Summary

Information
Class: AmbientServices.AmbientBottleneckSurveyorCoordinator
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Helpers/BottleneckDetectorHelpers.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 41
Uncovered lines: 0
Coverable lines: 41
Total lines: 281
Line coverage: 100%
Branch coverage
100%
Covered branches: 30
Total branches: 30
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
.ctor(...)100%44100%
CreateCallContextSurveyor(...)100%44100%
CreateTimeWindowSurveyor(...)100%66100%
CreateProcessSurveyor(...)100%44100%
CreateThreadSurveyor(...)100%44100%
Dispose(...)100%44100%
Dispose()100%11100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Helpers/BottleneckDetectorHelpers.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Runtime.CompilerServices;
 4using System.Runtime.Versioning;
 5using System.Text.RegularExpressions;
 6using System.Threading.Tasks;
 7
 8namespace AmbientServices;
 9
 10/// <summary>
 11/// A class that contains immutable information about a bottleneck, something that could potentially limit performance a
 12/// </summary>
 13/// <remarks>
 14/// <pitch>The declaration of one potential scalability limit — its identity, its limit (if known), and how utilization 
 15/// <pledge>
 16/// Instances are immutable and safe to share process-wide; the dash-delimited identifier (most generic classification f
 17/// An <see cref="Automatic"/> bottleneck measures usage as elapsed access time; a manual one records only what callers 
 18/// <see cref="EnterBottleneck"/> is null-tolerant: with no ambient detector configured it returns null and the access s
 19/// </pledge>
 20/// </remarks>
 21public class AmbientBottleneck
 22{
 23    private static readonly AmbientService<IAmbientBottleneckDetector> _BottleneckDetector = Ambient.GetService<IAmbient
 24    /// <summary>
 25    /// The identifier for the bottleneck.  The identifier is used in combination with regular expressions to filter whi
 26    /// </summary>
 27    /// <remarks>
 28    /// The identifier string is dash-delimited and starts with the most generic classification and progresses down to I
 29    /// </remarks>
 30    public string Id { get; private set; }
 31    /// <summary>
 32    /// A <see cref="AmbientBottleneckUtilizationAlgorithm"/> indicating the type of bottleneck.
 33    /// </summary>
 34    public AmbientBottleneckUtilizationAlgorithm UtilizationAlgorithm { get; private set; }
 35    /// <summary>
 36    /// Whether or not this bottleneck is measured automatically using elapsed time (in units of stopwatch ticks).
 37    /// If not automatic, no usage will be recorded unless either <see cref="AmbientBottleneckAccessor.SetUsage"/> or <s
 38    /// </summary>
 39    public bool Automatic { get; private set; }
 40    /// <summary>
 41    /// A human-readable description of the bottleneck.
 42    /// </summary>
 43    public string Description { get; private set; }
 44    /// <summary>
 45    /// The limit that is enforced, if any.  See <see cref="AmbientBottleneckUtilizationAlgorithm"/> for what this value
 46    /// For automatic bottlenecks, the limit is in terms of stopwatch ticks.
 47    /// </summary>
 48    public double? Limit { get; private set; }
 49    /// <summary>
 50    /// A <see cref="TimeSpan"/> indicating the period in which the limit is enforced, if any.
 51    /// </summary>
 52    public TimeSpan? LimitPeriod { get; private set; }
 53    /// <summary>
 54    /// Constructs an AmbientBottleneck with the specified properties.
 55    /// </summary>
 56    /// <param name="id">The string identifier for the bottleneck.</param>
 57    /// <param name="utilizationAlgorithm">An <see cref="AmbientBottleneckUtilizationAlgorithm"/> indicating the algorit
 58    /// <param name="automatic">Whether or not the bottleneck's usage is measured automatically or using <see cref="Ambi
 59    /// <param name="description">A description of the bottleneck.</param>
 60    /// <param name="limit">An optional limit (for automatic bottlenecks, in units of stopwatch ticks).</param>
 61    /// <param name="limitPeriod">A <see cref="TimeSpan"/> indicating the period during which the limit is applied.</par
 62    public AmbientBottleneck(string id, AmbientBottleneckUtilizationAlgorithm utilizationAlgorithm, bool automatic, stri
 63    {
 64        if (limit <= 0.0) throw new ArgumentOutOfRangeException(nameof(limit), "The bottleneck limit must be null or pos
 65        if (limitPeriod?.Ticks <= 0.0) throw new ArgumentOutOfRangeException(nameof(limitPeriod), "The bottleneck limitP
 66        Id = id;
 67        UtilizationAlgorithm = utilizationAlgorithm;
 68        Automatic = automatic;
 69        Description = description;
 70        Limit = limit;
 71        LimitPeriod = limitPeriod;
 72    }
 73    /// <summary>
 74    /// Enters the bottleneck, if there is a configured detector.
 75    /// </summary>
 76    /// <returns>An <see cref="AmbientBottleneckAccessor"/> that should be disposed when exiting the bottleneck, or null
 77    public AmbientBottleneckAccessor? EnterBottleneck()
 78    {
 79        return _BottleneckDetector.Local?.EnterBottleneck(this);
 80    }
 81    /// <summary>
 82    /// Gets a string that represents this object.
 83    /// </summary>
 84    /// <returns>A string that represents this object.</returns>
 85    public override string ToString()
 86    {
 87        return Id;
 88    }
 89}
 90/// <summary>
 91/// An interface that abstracts a survey of bottleneck statistics.
 92/// </summary>
 93/// <remarks>
 94/// <pitch>The read side of bottleneck detection: for one surveyed scope (a call context, a thread, a time window, or a 
 95/// <pledge>
 96/// Each bottleneck accessed within the scope appears as a single combined <see cref="AmbientBottleneckAccessor"/> aggre
 97/// Results may be read while the survey is still collecting; realizations are generally not thread-safe to read.
 98/// </pledge>
 99/// </remarks>
 100public interface IAmbientBottleneckSurvey
 101{
 102    /// <summary>
 103    /// Gets the name of the scope that was surveyed.
 104    /// </summary>
 105    string ScopeName { get; }
 106    /// <summary>
 107    /// Gets the <see cref="AmbientBottleneckAccessor"/> that was utilized the most, if any were used.
 108    /// </summary>
 109    AmbientBottleneckAccessor? MostUtilizedBottleneck { get; }
 110    /// <summary>
 111    /// Gets the most used <see cref="AmbientBottleneckAccessor"/> within the scope.
 112    /// </summary>
 113    /// <param name="count">The number of top limits to get.  Due to the potentially-large number of bottlenecks and the
 114    /// <returns>An enumeration of <see cref="AmbientBottleneckAccessor"/>s for the most utilized bottlenecks.</returns>
 115    IEnumerable<AmbientBottleneckAccessor> GetMostUtilizedBottlenecks(int count);
 116}
 117/// <summary>
 118/// An interface that combines <see cref="IAmbientBottleneckSurvey"/> and <see cref="IDisposable"/> in order to scope th
 119/// </summary>
 120/// <remarks>
 121/// <pitch>A survey whose collection window the caller brackets: construction starts collecting, disposal stops; results
 122/// <pledge><see cref="IAmbientBottleneckSurvey"/></pledge>
 123/// </remarks>
 124public interface IAmbientBottleneckSurveyor : IAmbientBottleneckSurvey, IDisposable
 125{
 126}
 127/// <summary>
 128/// A class that manages bottleneck surveyors.
 129/// </summary>
 130/// <remarks>
 131/// <pitch>The factory you use to turn the raw <see cref="IAmbientBottleneckDetector"/> access stream into actual survey
 132/// <pledge>
 133/// Surveyors created by one coordinator observe accesses reported to the ambient bottleneck detector captured at the co
 134/// Default allow/block filters come from the ambient settings set as regex strings matched against bottleneck identifie
 135/// </pledge>
 136/// <plan>
 137/// Call-context and thread surveys are served by long-lived <see cref="CallContextSurveyManager"/> and <see cref="Threa
 138/// </plan>
 139/// </remarks>
 140public class AmbientBottleneckSurveyorCoordinator : IDisposable
 141{
 2142    private static readonly AmbientService<IAmbientSettingsSet> _SettingsSet = Ambient.GetService<IAmbientSettingsSet>()
 2143    private static readonly AmbientService<IAmbientBottleneckDetector> _AmbientBottleneckDetector = Ambient.GetService<I
 144
 145    private readonly IAmbientSetting<Regex?> _defaultAllowSetting;
 146    private readonly IAmbientSetting<Regex?> _defaultBlockSetting;
 147    private readonly IAmbientBottleneckDetector? _bottleneckDetector;
 148    private readonly CallContextSurveyManager _callContextSurveyor;
 149    private readonly ThreadSurveyManager _threadSurveyor;
 150    private bool _disposedValue;
 151
 152    /// <summary>
 153    /// Constructs a AmbientBottleneckTracker using the ambient settings set.
 154    /// </summary>
 155    public AmbientBottleneckSurveyorCoordinator()
 2156        : this(_SettingsSet.Local)
 157    {
 2158    }
 159    /// <summary>
 160    /// Constructs a AmbientBottleneckTracker using the specified settings set.
 161    /// </summary>
 162    /// <param name="settingsSet">An <see cref="IAmbientSettingsSet"/> to get settings from.</param>
 2163    public AmbientBottleneckSurveyorCoordinator(IAmbientSettingsSet? settingsSet)
 164    {
 2165        _defaultAllowSetting = AmbientSettings.GetSetting<Regex?>(settingsSet, nameof(AmbientBottleneckSurveyorCoordinat
 2166            @"A `Regex` string used to match bottleneck identifiers that should be tracked.  By default, all bottlenecks
 2167            s => string.IsNullOrEmpty(s) ? null : new Regex(s, RegexOptions.Compiled));
 2168        _defaultBlockSetting = AmbientSettings.GetSetting<Regex?>(settingsSet, nameof(AmbientBottleneckSurveyorCoordinat
 2169            @"A `Regex` string used to match bottleneck identifiers that should NOT be tracked.  By default, no bottlene
 2170            s => string.IsNullOrEmpty(s) ? null : new Regex(s, RegexOptions.Compiled));
 2171        _bottleneckDetector = _AmbientBottleneckDetector.Local;
 2172        _callContextSurveyor = new CallContextSurveyManager(_bottleneckDetector);
 2173        _threadSurveyor = new ThreadSurveyManager(_bottleneckDetector);
 2174    }
 175    /// <summary>
 176    /// Creates a call context bottleneck survey.
 177    /// </summary>
 178    /// <param name="scopeName">A name of the call context to attach to the analyzer.  Defaults to the name of the calli
 179    /// <param name="overrideAllowRegex">A <see cref="Regex"/> string to override the default allow filter.</param>
 180    /// <param name="overrideBlockRegex">A <see cref="Regex"/> string to override the default block filter.</param>
 181    /// <returns>A <see cref="IAmbientBottleneckSurveyor"/> that surveys bottleneck statistics for this call context.  N
 182    public IAmbientBottleneckSurveyor CreateCallContextSurveyor([CallerMemberName] string? scopeName = null, string? ove
 183    {
 2184        return _callContextSurveyor.CreateCallContextSurveyor(scopeName,
 2185                (overrideAllowRegex == null) ? _defaultAllowSetting.Value : new Regex(overrideAllowRegex, RegexOptions.C
 2186               (overrideBlockRegex == null) ? _defaultBlockSetting.Value : new Regex(overrideBlockRegex, RegexOptions.Co
 2187            );
 188    }
 189    /// <summary>
 190    /// Creates a bottleneck survey generator that generates bottleneck statistics surveys for periodic time windows unt
 191    /// Note that the name of each window's scope is generated automatically.
 192    /// </summary>
 193    /// <param name="windowSize">The size of the temporal windows that will be used for contention tracking.</param>
 194    /// <param name="onWindowComplete">An async delegate that is invoked whenever a time window has ended, making a new 
 195    /// <param name="overrideAllowRegex">A <see cref="Regex"/> string to override the default allow filter.</param>
 196    /// <param name="overrideBlockRegex">A <see cref="Regex"/> string to override the default block filter.</param>
 197    /// <returns>A <see cref="IDisposable"/> that scopes the collection of the surveys.</returns>
 198    public IDisposable CreateTimeWindowSurveyor(TimeSpan windowSize, Func<IAmbientBottleneckSurvey, Task> onWindowComple
 199    {
 2200        if (onWindowComplete == null) throw new ArgumentNullException(nameof(onWindowComplete), "Time Window Surveys are
 2201        Regex? allow = (overrideAllowRegex == null) ? _defaultAllowSetting.Value : new Regex(overrideAllowRegex, RegexOp
 2202        Regex? block = (overrideBlockRegex == null) ? _defaultBlockSetting.Value : new Regex(overrideBlockRegex, RegexOp
 2203        return new TimeWindowSurveyManager(windowSize, onWindowComplete, _bottleneckDetector, allow, block);
 204    }
 205    /// <summary>
 206    /// Creates a bottleneck survey which analyzes limit proximities for everything in the process until the process ter
 207    /// Note that this is only useful to determine the limits for an entire process from beginning to end, which is not 
 208    /// <see cref="CreateTimeWindowSurveyor"/> is a better match in most situations.
 209    /// </summary>
 210    /// <param name="processScopeName">The name of the thread scope, or <b>null</b> to automatically build one with the 
 211    /// <param name="overrideAllowRegex">A <see cref="Regex"/> string to override the default allow filter.</param>
 212    /// <param name="overrideBlockRegex">A <see cref="Regex"/> string to override the default block filter.</param>
 213    /// <returns>A <see cref="IAmbientBottleneckSurveyor"/> that surveys bottleneck statistics survey for the entire pro
 214    /// <remarks>
 215    /// This is different from using <see cref="CreateCallContextSurveyor"/> because that will only survey the call cont
 216    /// whereas this will survey all threads and call contexts in the process.
 217    /// They will produce the same results only for programs where there is only a single call context (no parallelizati
 218    /// </remarks>
 219#if NET5_0_OR_GREATER
 220    [UnsupportedOSPlatform("browser")]
 221#endif
 222    public IAmbientBottleneckSurveyor CreateProcessSurveyor(string? processScopeName = null, string? overrideAllowRegex 
 223    {
 2224        ProcessBottleneckSurveyor analyzer = new(processScopeName, _bottleneckDetector,
 2225                (overrideAllowRegex == null) ? _defaultAllowSetting.Value : new Regex(overrideAllowRegex, RegexOptions.C
 2226                (overrideBlockRegex == null) ? _defaultBlockSetting.Value : new Regex(overrideBlockRegex, RegexOptions.C
 2227            );
 2228        return analyzer;
 229    }
 230    /// <summary>
 231    /// Creates a bottleneck survey which tracks limit proximities for everything in the thread until the thread termina
 232    /// Note that <see cref="CreateTimeWindowSurveyor"/> is a better match in most situations.
 233    /// </summary>
 234    /// <param name="threadScopeName">The name of the thread scope, or <b>null</b> to automatically build one with the n
 235    /// <param name="overrideAllowRegex">A <see cref="Regex"/> string to override the default allow filter.</param>
 236    /// <param name="overrideBlockRegex">A <see cref="Regex"/> string to override the default block filter.</param>
 237    /// <returns>A <see cref="IAmbientBottleneckSurveyor"/> that surveys bottleneck statistics survey for the current th
 238    public IAmbientBottleneckSurveyor CreateThreadSurveyor(string? threadScopeName = null, string? overrideAllowRegex = 
 239    {
 2240        return _threadSurveyor.CreateThreadSurveyor(threadScopeName,
 2241                (overrideAllowRegex == null) ? _defaultAllowSetting.Value : new Regex(overrideAllowRegex, RegexOptions.C
 2242               (overrideBlockRegex == null) ? _defaultBlockSetting.Value : new Regex(overrideBlockRegex, RegexOptions.Co
 2243            );
 244    }
 245    /// <summary>
 246    /// Disposes of the instance.
 247    /// </summary>
 248    /// <param name="disposing">Whether the instance is being disposed (as opposed to finalized).</param>
 249    protected virtual void Dispose(bool disposing)
 250    {
 2251        if (!_disposedValue)
 252        {
 2253            if (disposing)
 254            {
 255                // TODO: dispose managed state (managed objects)
 2256                _callContextSurveyor.Dispose();
 2257                _threadSurveyor.Dispose();
 258            }
 259
 260            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
 261            // TODO: set large fields to null
 2262            _disposedValue = true;
 263        }
 2264    }
 265
 266    // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
 267    // ~AmbientBottleneckSurveyorCoordinator()
 268    // {
 269    //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 270    //     Dispose(disposing: false);
 271    // }
 272    /// <summary>
 273    /// Disposes of the instance.
 274    /// </summary>
 275    public void Dispose()
 276    {
 277        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 2278        Dispose(disposing: true);
 2279        GC.SuppressFinalize(this);
 2280    }
 281}