1
- using RabbitMQ . Client . Impl ;
2
-
3
- using System . Collections . Concurrent ;
1
+ using System . Collections . Concurrent ;
2
+ using System . Reflection ;
4
3
using System . Threading ;
5
4
using System . Threading . Tasks ;
6
5
6
+ using RabbitMQ . Client . Impl ;
7
+
7
8
namespace RabbitMQ . Client
8
9
{
9
- internal class AsyncConsumerWorkService : ConsumerWorkService
10
+ internal sealed class AsyncConsumerWorkService : ConsumerWorkService
10
11
{
11
- private readonly ConcurrentDictionary < IModel , WorkPool > workPools = new ConcurrentDictionary < IModel , WorkPool > ( ) ;
12
+ private readonly ConcurrentDictionary < IModel , WorkPool > _workPools = new ConcurrentDictionary < IModel , WorkPool > ( ) ;
12
13
13
14
public void Schedule < TWork > ( ModelBase model , TWork work ) where TWork : Work
14
15
{
15
- workPools . GetOrAdd ( model , StartNewWorkPool ) . Enqueue ( work ) ;
16
+ _workPools . GetOrAdd ( model , StartNewWorkPool ) . Enqueue ( work ) ;
16
17
}
17
18
18
19
private WorkPool StartNewWorkPool ( IModel model )
@@ -24,69 +25,94 @@ private WorkPool StartNewWorkPool(IModel model)
24
25
25
26
public void Stop ( IModel model )
26
27
{
27
- if ( workPools . TryRemove ( model , out WorkPool workPool ) )
28
+ if ( _workPools . TryRemove ( model , out WorkPool workPool ) )
28
29
{
29
30
workPool . Stop ( ) ;
30
31
}
31
32
}
32
33
33
34
public void Stop ( )
34
35
{
35
- foreach ( IModel model in workPools . Keys )
36
+ foreach ( IModel model in _workPools . Keys )
36
37
{
37
38
Stop ( model ) ;
38
39
}
39
40
}
40
41
41
42
class WorkPool
42
43
{
43
- readonly ConcurrentQueue < Work > workQueue ;
44
- readonly CancellationTokenSource tokenSource ;
45
- readonly ModelBase model ;
46
- readonly SemaphoreSlim semaphore = new SemaphoreSlim ( 0 ) ;
47
- private Task task ;
44
+ readonly ConcurrentQueue < Work > _workQueue ;
45
+ readonly CancellationTokenSource _tokenSource ;
46
+ readonly ModelBase _model ;
47
+ readonly CancellationTokenRegistration _tokenRegistration ;
48
+ volatile TaskCompletionSource < bool > _syncSource = TaskCompletionSourceFactory . Create < bool > ( ) ;
49
+ private Task _worker ;
48
50
49
51
public WorkPool ( ModelBase model )
50
52
{
51
- this . model = model ;
52
- workQueue = new ConcurrentQueue < Work > ( ) ;
53
- tokenSource = new CancellationTokenSource ( ) ;
53
+ _model = model ;
54
+ _workQueue = new ConcurrentQueue < Work > ( ) ;
55
+ _tokenSource = new CancellationTokenSource ( ) ;
56
+ _tokenRegistration = _tokenSource . Token . Register ( ( ) => _syncSource . TrySetCanceled ( ) ) ;
54
57
}
55
58
56
59
public void Start ( )
57
60
{
58
- task = Task . Run ( Loop , CancellationToken . None ) ;
61
+ _worker = Task . Run ( Loop , CancellationToken . None ) ;
59
62
}
60
63
61
64
public void Enqueue ( Work work )
62
65
{
63
- workQueue . Enqueue ( work ) ;
64
- semaphore . Release ( ) ;
66
+ _workQueue . Enqueue ( work ) ;
67
+ _syncSource . TrySetResult ( true ) ;
65
68
}
66
69
67
70
async Task Loop ( )
68
71
{
69
- while ( tokenSource . IsCancellationRequested == false )
72
+ while ( _tokenSource . IsCancellationRequested == false )
70
73
{
71
74
try
72
75
{
73
- await semaphore . WaitAsync ( tokenSource . Token ) . ConfigureAwait ( false ) ;
76
+ await _syncSource . Task . ConfigureAwait ( false ) ;
77
+ _syncSource = TaskCompletionSourceFactory . Create < bool > ( ) ;
74
78
}
75
79
catch ( TaskCanceledException )
76
80
{
77
81
// Swallowing the task cancellation in case we are stopping work.
78
82
}
79
83
80
- if ( ! tokenSource . IsCancellationRequested && workQueue . TryDequeue ( out Work work ) )
84
+ while ( _tokenSource . IsCancellationRequested == false && _workQueue . TryDequeue ( out Work work ) )
81
85
{
82
- await work . Execute ( model ) . ConfigureAwait ( false ) ;
86
+ await work . Execute ( _model ) . ConfigureAwait ( false ) ;
83
87
}
84
88
}
85
89
}
86
90
87
91
public void Stop ( )
88
92
{
89
- tokenSource . Cancel ( ) ;
93
+ _tokenSource . Cancel ( ) ;
94
+ _tokenRegistration . Dispose ( ) ;
95
+ }
96
+
97
+ static class TaskCompletionSourceFactory
98
+ {
99
+ #if NETFRAMEWORK
100
+ static readonly FieldInfo StateField = typeof ( Task ) . GetField ( "m_stateFlags" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
101
+ #endif
102
+
103
+ public static TaskCompletionSource < T > Create < T > ( TaskCreationOptions options = TaskCreationOptions . None )
104
+ {
105
+ #if NETFRAMEWORK
106
+ //This lovely hack forces the task scheduler to run continuations asynchronously,
107
+ //see https://stackoverflow.com/questions/22579206/how-can-i-prevent-synchronous-continuations-on-a-task/22588431#22588431
108
+ var tcs = new TaskCompletionSource < T > ( options ) ;
109
+ const int TASK_STATE_THREAD_WAS_ABORTED = 134217728 ;
110
+ StateField . SetValue ( tcs . Task , ( int ) StateField . GetValue ( tcs . Task ) | TASK_STATE_THREAD_WAS_ABORTED ) ;
111
+ return tcs ;
112
+ #else
113
+ return new TaskCompletionSource < T > ( options | TaskCreationOptions . RunContinuationsAsynchronously ) ;
114
+ #endif
115
+ }
90
116
}
91
117
}
92
118
}
0 commit comments