• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bitfaster / BitFaster.Caching / 24098901250

07 Apr 2026 06:54PM UTC coverage: 98.9% (-0.2%) from 99.103%
24098901250

Pull #750

github

web-flow
Merge 2426eed78 into f39fc4482
Pull Request #750: BackgroundSchedulerTests stability

1249 of 1286 branches covered (97.12%)

Branch coverage included in aggregate %.

3 of 6 new or added lines in 1 file covered. (50.0%)

6 existing lines in 3 files now uncovered.

5317 of 5353 relevant lines covered (99.33%)

59513794.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.0
/BitFaster.Caching/Scheduler/BackgroundThreadScheduler.cs
1
using System;
2
using System.Threading;
3
using System.Threading.Tasks;
4
using BitFaster.Caching.Buffers;
5

6
namespace BitFaster.Caching.Scheduler
7
{
8
    /// <summary>
9
    /// Represents a scheduler that handles queuing tasks on a long running background thread.
10
    /// </summary>
11
    /// <remarks>
12
    /// Goals:
13
    /// 1. Background thread awaits work, does not block a thread pool thread.
14
    /// 2. Does not allocate when scheduling.
15
    /// 3. Is faster than Task.Run/TaskFactory.StartNew.
16
    /// </remarks>
17
    public sealed class BackgroundThreadScheduler : IScheduler, IDisposable
18
    {
19
        /// <summary>
20
        /// The maximum number of work items to store.
21
        /// </summary>
22
        public const int MaxBacklog = 16;
23

24
        private int count;
25
        private readonly CancellationTokenSource cts = new();
800✔
26
        private readonly SemaphoreSlim semaphore = new(0, MaxBacklog);
800✔
27
        private readonly MpmcBoundedBuffer<Action> work = new(MaxBacklog);
800✔
28

29
        private Optional<Exception> lastException = Optional<Exception>.None();
800✔
30
        private readonly Task completion;
31

32
        /// <summary>
33
        /// Initializes a new instance of the BackgroundThreadScheduler class.
34
        /// </summary>
35
        public BackgroundThreadScheduler()
800✔
36
        {
800✔
37
            // dedicated thread
38
            this.completion = Task.Factory.StartNew(async () => await Background().ConfigureAwait(false), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
1,600✔
39
        }
800✔
40

41
        ///<inheritdoc/>
42
        public Task Completion => completion;
455✔
43

44
        ///<inheritdoc/>
45
        public bool IsBackground => true;
21,270,132✔
46

47
        ///<inheritdoc/>
48
        public long RunCount => count;
30✔
49

50
        ///<inheritdoc/>
51
        public Optional<Exception> LastException => lastException;
337✔
52

53
        ///<inheritdoc/>
54
        public void Run(Action action)
55
        {
6,113,134✔
56
            if (work.TryAdd(action) == BufferStatus.Success)
6,113,134✔
57
            {
4,800,031✔
58
                semaphore.Release();
4,800,031✔
59
                count++;
4,800,031✔
60
            }
4,800,031✔
61
        }
6,113,134✔
62

63
        private async Task Background()
64
        {
800✔
65
            while (true)
4,800,602✔
66
            {
4,800,602✔
67
                try
68
                {
4,800,602✔
69
                    await semaphore.WaitAsync(cts.Token).ConfigureAwait(false);
4,800,602✔
70

71
                    var spinner = new SpinWait();
4,799,802✔
72
                    BufferStatus s;
73
                    do
74
                    {
4,799,802✔
75
                        s = work.TryTake(out var action);
4,799,802✔
76

77
                        if (s == BufferStatus.Success)
4,799,802!
78
                        {
4,799,802✔
79
                            action!();
4,799,802✔
80
                        }
4,799,797✔
NEW
81
                        else if (s == BufferStatus.Empty)
×
NEW
82
                        {
×
NEW
83
                            break;
×
84
                        }
85
                        else
86
                        {
×
87
                            spinner.SpinOnce();
×
88
                        }
×
89
                    }
4,799,797✔
90
                    while (s == BufferStatus.Contended);
4,799,797✔
91

92
                }
4,799,797✔
93
                catch (OperationCanceledException)
490✔
94
                {
490✔
95
                    break;
490✔
96
                }
97
                catch (Exception ex)
5✔
98
                {
5✔
99
                    this.lastException = new Optional<Exception>(ex);
5✔
100
                }
5✔
101
            }
4,799,802✔
102
        }
490✔
103

104
        /// <summary>
105
        /// Terminate the background thread.
106
        /// </summary>
107
        public void Dispose()
108
        {
550✔
109
            // prevent hang when cancel runs on the same thread
110
            this.cts.CancelAfter(TimeSpan.Zero);
550✔
111
        }
550✔
112
    }
113
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc