< Summary

Information
Class: CounterpointCollective.Dataflow.TokenGateBlock<T>
Assembly: Dataflow.Composable
File(s): /builds/counterpointcollective/composabledataflowblocks/Source/Dataflow.Composable/DataFlow/TokenGateBlock.cs
Line coverage
87%
Covered lines: 29
Uncovered lines: 4
Coverable lines: 33
Total lines: 94
Line coverage: 87.8%
Branch coverage
75%
Covered branches: 9
Total branches: 12
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_TargetSide()100%11100%
get_CompletionSide()100%11100%
.ctor(...)100%11100%
Allow(...)100%11100%
get_Tokens()100%11100%
.ctor(...)100%11100%
get_Tokens()100%11100%
TryReservePendingPayment()75%4483.33%
CommitPendingPayment(...)50%5466.66%
Allow(...)100%44100%

File(s)

/builds/counterpointcollective/composabledataflowblocks/Source/Dataflow.Composable/DataFlow/TokenGateBlock.cs

#LineLine coverage
 1using CounterpointCollective.Dataflow.Encapsulation;
 2using System.Threading.Tasks.Dataflow;
 3namespace CounterpointCollective.Dataflow
 4{
 5    public class TokenGateBlock<T> : AbstractEncapsulatedTargetBlock<T>, ITargetBlock<T>
 6    {
 7        /// <exclude/>
 10013888        protected override ITargetBlock<T> TargetSide => _inner;
 9        /// <exclude/>
 110        protected override IDataflowBlock CompletionSide => _inner;
 11
 12        private readonly AdmissionGateBlock<T> _inner;
 13        private readonly State _state;
 14        private readonly Action<int>? _onTokenSpent;
 15
 2816        public TokenGateBlock(ITargetBlock<T> inner, Action<int>? onTokenSpent = null)
 17        {
 2818            _state = new(this);
 2819            _onTokenSpent = onTokenSpent;
 2820            var hooks = new AdmissionGateHooks()
 2821            {
 110175922                MayTryToEnter = () => _state.TryReservePendingPayment(),
 100136223                Entering = () => _state.CommitPendingPayment(true),
 024                FailingToEnter = () => _state.CommitPendingPayment(false)
 2825            };
 2826            _inner = new AdmissionGateBlock<T>(inner, hooks);
 2827        }
 28
 29        /// <summary>
 30        /// Allow count new messages to be delivered (either synchronously or asynchronously) by the linked targets.
 31        /// /</summary>
 10076532        public void Allow(int count = 1) => _state.Allow(count);
 33
 34        /// <summary>
 35        /// Current amount of tokens still available.
 36        /// </summary>
 100000737        public int Tokens => _state.Tokens;
 38
 39        private class State
 40        {
 5641            public State(TokenGateBlock<T> outer) => _outer = outer;
 42
 43            private readonly TokenGateBlock<T> _outer;
 44
 100000745            public int Tokens => tokens;
 46
 47            private volatile int tokens;
 48            private int reserved;
 49
 50            /// <summary>
 51            /// Must be followed by a call to CommitPendingPayment.
 52            /// </summary>
 53            public bool TryReservePendingPayment()
 54            {
 110175955                while(tokens - reserved > 0)
 56                {
 100136257                    reserved++;
 100136258                    if (tokens - reserved < 0)
 59                    {
 060                        reserved--;
 61                    }
 62                    else
 63                    {
 100136264                        return true;
 65                    }
 66                }
 10039767                return false;
 68            }
 69
 70            public void CommitPendingPayment(bool consume)
 71            {
 100136272                reserved--;
 100136273                if (consume)
 74                {
 100136275                    Interlocked.Decrement(ref tokens);
 100136276                    _outer._onTokenSpent?.Invoke(Tokens);
 77                }
 78                else
 79                {
 080                    _outer._inner.ProcessPostponedMessages();
 81                }
 082            }
 83
 84            public void Allow(int i = 1)
 85            {
 10076586                var v = Interlocked.Add(ref tokens, i);
 10076587                if (i > 0 && v > 0)
 88                {
 10076589                    _outer._inner.ProcessPostponedMessages();
 90                }
 10076591            }
 92        }
 93    }
 94}