< Summary

Information
Class: MyNet.UI.Loading.BusyService
Assembly: MyNet.UI
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.UI/Loading/BusyService.cs
Tag: 323_28699572109
Line coverage
84%
Covered lines: 39
Uncovered lines: 7
Coverable lines: 46
Total lines: 139
Line coverage: 84.7%
Branch coverage
66%
Covered branches: 8
Total branches: 12
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_IsBusy()100%11100%
get_CurrentBusy()100%22100%
RunAsync()100%11100%
RunAsync()100%210%
Begin(...)100%11100%
GetCurrent()100%22100%
Pop(...)50%4487.5%
Bind(...)50%2271.42%
BeginScope(...)100%11100%
RaiseStateChanged()100%11100%
DisposeResources()50%22100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.UI/Loading/BusyService.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="BusyService.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Generic;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using MyNet.Observable;
 12using MyNet.UI.Loading.Models;
 13
 14namespace MyNet.UI.Loading;
 15
 16/// <summary>
 17/// Default <see cref="IBusyService"/> implementation with nested scopes and cancellation support.
 18/// </summary>
 19public sealed class BusyService : ObservableObject, IBusyService
 20{
 48021    private readonly Lock _gate = new();
 48022    private readonly Stack<BusyStackEntry> _stack = new();
 23
 24    /// <inheritdoc />
 25    public bool IsBusy
 26    {
 27        get
 8128        {
 29            lock (_gate)
 8130                return _stack.Count > 0;
 8131        }
 32    }
 33
 34    /// <inheritdoc />
 35    public IBusy? CurrentBusy
 36    {
 37        get
 938        {
 39            lock (_gate)
 940                return _stack.Count > 0 ? _stack.Peek().Busy : null;
 941        }
 42    }
 43
 44    /// <inheritdoc />
 45    public async Task RunAsync<TBusy>(Func<TBusy, CancellationToken, Task> action, CancellationToken cancellationToken =
 46        where TBusy : IBusy, new()
 47    {
 10248        ArgumentNullException.ThrowIfNull(action);
 49
 10250        using var scope = BeginScope<TBusy>(cancellationToken);
 10251        await action((TBusy)scope.Busy, scope.CancellationToken).ConfigureAwait(false);
 9352    }
 53
 54    /// <inheritdoc />
 55    public async Task<TResult> RunAsync<TBusy, TResult>(Func<TBusy, CancellationToken, Task<TResult>> action, Cancellati
 56        where TBusy : IBusy, new()
 57    {
 058        ArgumentNullException.ThrowIfNull(action);
 59
 060        using var scope = BeginScope<TBusy>(cancellationToken);
 061        return await action((TBusy)scope.Busy, scope.CancellationToken).ConfigureAwait(false);
 062    }
 63
 64    /// <inheritdoc />
 65    public IBusyScope Begin<TBusy>(CancellationToken cancellationToken = default)
 66        where TBusy : IBusy, new()
 667        => BeginScope<TBusy>(cancellationToken);
 68
 69    /// <inheritdoc />
 70    public TBusy? GetCurrent<TBusy>()
 71        where TBusy : class, IBusy
 5472    {
 73        lock (_gate)
 5474            return _stack.Count > 0 ? _stack.Peek().Busy as TBusy : null;
 5475    }
 76
 77    internal void Pop(BusyScope scope)
 78    {
 10879        ArgumentNullException.ThrowIfNull(scope);
 80
 81        lock (_gate)
 82        {
 10883            if (_stack.Count == 0 || !ReferenceEquals(_stack.Peek().Scope, scope))
 084                return;
 85
 10886            var entry = _stack.Pop();
 10887            entry.DisposeResources();
 10888            RaiseStateChanged();
 10889        }
 10890    }
 91
 92    private static CancellationToken Bind<TBusy>(TBusy busy, CancellationToken cancellationToken, out CancellationTokenS
 93        where TBusy : IBusy
 94    {
 10895        ArgumentNullException.ThrowIfNull(busy);
 96
 10897        if (busy is not Busy bindable)
 98        {
 099            linkedSource = null;
 0100            return cancellationToken;
 101        }
 102
 108103        linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
 108104        bindable.BindCancellation(linkedSource);
 108105        return linkedSource.Token;
 106    }
 107
 108    private BusyScope BeginScope<TBusy>(CancellationToken cancellationToken)
 109        where TBusy : IBusy, new()
 110    {
 108111        var busy = new TBusy();
 108112        var token = Bind(busy, cancellationToken, out var linkedSource);
 108113        var scope = new BusyScope(this, busy, token);
 114
 115        lock (_gate)
 116        {
 108117            _stack.Push(new(busy, linkedSource, scope));
 108118            RaiseStateChanged();
 108119        }
 120
 108121        return scope;
 122    }
 123
 124    private void RaiseStateChanged()
 125    {
 216126        NotifyPropertyChanged(nameof(IsBusy));
 216127        NotifyPropertyChanged(nameof(CurrentBusy));
 216128    }
 129
 130    private sealed class BusyStackEntry(IBusy busy, CancellationTokenSource? linkedSource, BusyScope scope)
 131    {
 132        public IBusy Busy { get; } = busy;
 133
 134        public BusyScope Scope { get; } = scope;
 135
 108136        public void DisposeResources() => linkedSource?.Dispose();
 137    }
 138}
 139