1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
3
using System . Threading ;
4
+ using System . Threading . Channels ;
4
5
using System . Threading . Tasks ;
5
6
6
7
namespace RabbitMQ . Client . Impl
@@ -61,20 +62,16 @@ internal Task StopWorkAsync(IModel model)
61
62
62
63
class WorkPool
63
64
{
64
- readonly ConcurrentQueue < Action > _actions ;
65
- readonly CancellationTokenSource _tokenSource ;
66
- readonly CancellationTokenRegistration _tokenRegistration ;
67
- volatile TaskCompletionSource < bool > _syncSource = new TaskCompletionSource < bool > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
65
+ private readonly Channel < Action > _channel ;
68
66
private readonly int _concurrency ;
69
67
private Task _worker ;
68
+ CancellationTokenSource _tokenSource ;
70
69
private SemaphoreSlim _limiter ;
71
70
72
71
public WorkPool ( int concurrency )
73
72
{
74
73
_concurrency = concurrency ;
75
- _actions = new ConcurrentQueue < Action > ( ) ;
76
- _tokenSource = new CancellationTokenSource ( ) ;
77
- _tokenRegistration = _tokenSource . Token . Register ( ( ) => _syncSource . TrySetCanceled ( ) ) ;
74
+ _channel = Channel . CreateUnbounded < Action > ( new UnboundedChannelOptions { SingleReader = true , SingleWriter = false , AllowSynchronousContinuations = false } ) ;
78
75
}
79
76
80
77
public void Start ( )
@@ -86,37 +83,27 @@ public void Start()
86
83
else
87
84
{
88
85
_limiter = new SemaphoreSlim ( _concurrency ) ;
86
+ _tokenSource = new CancellationTokenSource ( ) ;
89
87
_worker = Task . Run ( ( ) => LoopWithConcurrency ( _tokenSource . Token ) , CancellationToken . None ) ;
90
88
}
91
89
}
92
90
93
91
public void Enqueue ( Action action )
94
92
{
95
- _actions . Enqueue ( action ) ;
96
- _syncSource . TrySetResult ( true ) ;
93
+ _channel . Writer . TryWrite ( action ) ;
97
94
}
98
95
99
96
async Task Loop ( )
100
97
{
101
- while ( _tokenSource . IsCancellationRequested == false )
98
+ while ( await _channel . Reader . WaitToReadAsync ( ) . ConfigureAwait ( false ) )
102
99
{
103
- try
104
- {
105
- await _syncSource . Task . ConfigureAwait ( false ) ;
106
- _syncSource = new TaskCompletionSource < bool > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
107
- }
108
- catch ( TaskCanceledException )
109
- {
110
- // Swallowing the task cancellation exception for the semaphore in case we are stopping.
111
- }
112
-
113
- while ( _actions . TryDequeue ( out Action action ) )
100
+ while ( _channel . Reader . TryRead ( out Action work ) )
114
101
{
115
102
try
116
103
{
117
- action ( ) ;
104
+ work ( ) ;
118
105
}
119
- catch ( Exception )
106
+ catch ( Exception )
120
107
{
121
108
// ignored
122
109
}
@@ -126,36 +113,37 @@ async Task Loop()
126
113
127
114
async Task LoopWithConcurrency ( CancellationToken cancellationToken )
128
115
{
129
- while ( _tokenSource . IsCancellationRequested == false )
116
+ try
130
117
{
131
- try
132
- {
133
- await _syncSource . Task . ConfigureAwait ( false ) ;
134
- _syncSource = new TaskCompletionSource < bool > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
135
- }
136
- catch ( TaskCanceledException )
118
+ while ( await _channel . Reader . WaitToReadAsync ( cancellationToken ) . ConfigureAwait ( false ) )
137
119
{
138
- // Swallowing the task cancellation exception for the semaphore in case we are stopping.
139
- }
140
-
141
- while ( _actions . TryDequeue ( out Action action ) )
142
- {
143
- // Do a quick synchronous check before we resort to async/await with the state-machine overhead.
144
- if ( ! _limiter . Wait ( 0 ) )
120
+ while ( _channel . Reader . TryRead ( out Action action ) )
145
121
{
146
- await _limiter . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
147
- }
122
+ // Do a quick synchronous check before we resort to async/await with the state-machine overhead.
123
+ if ( ! _limiter . Wait ( 0 ) )
124
+ {
125
+ await _limiter . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
126
+ }
148
127
149
- _ = OffloadToWorkerThreadPool ( action , _limiter ) ;
128
+ _ = OffloadToWorkerThreadPool ( action , _limiter ) ;
129
+ }
150
130
}
151
131
}
132
+ catch ( OperationCanceledException )
133
+ {
134
+ // ignored
135
+ }
152
136
}
153
137
154
138
static async Task OffloadToWorkerThreadPool ( Action action , SemaphoreSlim limiter )
155
139
{
156
140
try
157
141
{
158
- await Task . Run ( ( ) => action ( ) ) ;
142
+ // like Task.Run but doesn't closure allocate
143
+ await Task . Factory . StartNew ( state =>
144
+ {
145
+ ( ( Action ) state ) ( ) ;
146
+ } , action , CancellationToken . None , TaskCreationOptions . DenyChildAttach , TaskScheduler . Default ) ;
159
147
}
160
148
catch ( Exception )
161
149
{
@@ -169,8 +157,8 @@ static async Task OffloadToWorkerThreadPool(Action action, SemaphoreSlim limiter
169
157
170
158
public Task Stop ( )
171
159
{
172
- _tokenSource . Cancel ( ) ;
173
- _tokenRegistration . Dispose ( ) ;
160
+ _channel . Writer . Complete ( ) ;
161
+ _tokenSource ? . Cancel ( ) ;
174
162
_limiter ? . Dispose ( ) ;
175
163
return _worker ;
176
164
}
0 commit comments