@@ -98,9 +98,11 @@ public class Subscription: IEnumerable, IEnumerator, IDisposable {
98
98
public IModel Model { get { return m_model ; } }
99
99
100
100
protected string m_queueName ;
101
+ protected bool m_noAck ;
102
+
103
+ protected readonly object m_consumerLock = new object ( ) ;
101
104
protected volatile QueueingBasicConsumer m_consumer ;
102
105
protected string m_consumerTag ;
103
- protected bool m_noAck ;
104
106
protected volatile bool m_shouldDelete ;
105
107
106
108
///<summary>Retrieve the queue name we have subscribed to. May
@@ -127,9 +129,10 @@ public class Subscription: IEnumerable, IEnumerator, IDisposable {
127
129
protected BasicDeliverEventArgs m_latestEvent ;
128
130
129
131
///<summary>Returns the most recent value returned by Next(),
130
- ///or null when either no values have been retrieved yet, or
131
- ///the most recent value has already been Ack()ed. See also
132
- ///the documentation for Ack().</summary>
132
+ ///or null when either no values have been retrieved yet, the
133
+ ///end of the subscription has been reached, or the most
134
+ ///recent value has already been Ack()ed. See also the
135
+ ///documentation for Ack().</summary>
133
136
public BasicDeliverEventArgs LatestEvent { get { return m_latestEvent ; } }
134
137
135
138
///<summary>Creates a new Subscription in "noAck" mode,
@@ -220,22 +223,27 @@ public void Close()
220
223
{
221
224
try {
222
225
bool shouldCancelConsumer = false ;
223
- lock ( this ) {
226
+ bool shouldDelete = false ;
227
+
228
+ lock ( m_consumerLock ) {
224
229
if ( m_consumer != null ) {
225
230
shouldCancelConsumer = true ;
226
231
m_consumer = null ;
227
232
}
233
+
234
+ shouldDelete = m_shouldDelete ;
235
+ // We set m_shouldDelete false before attempting
236
+ // the delete, because trying twice is worse than
237
+ // trying once and failing.
238
+ m_shouldDelete = false ;
228
239
}
240
+
229
241
if ( shouldCancelConsumer ) {
230
242
m_model . BasicCancel ( m_consumerTag ) ;
231
243
m_consumerTag = null ;
232
244
}
233
245
234
- if ( m_shouldDelete ) {
235
- m_shouldDelete = false ;
236
- // We set m_shouldDelete false before attempting
237
- // the delete, because trying twice is worse than
238
- // trying once and failing.
246
+ if ( shouldDelete ) {
239
247
m_model . QueueDelete ( m_queueName , false , false , false ) ;
240
248
}
241
249
} catch ( OperationInterruptedException ) {
@@ -324,11 +332,15 @@ public void Ack(BasicDeliverEventArgs evt)
324
332
public BasicDeliverEventArgs Next ( )
325
333
{
326
334
try {
327
- if ( m_consumer == null ) {
335
+ // Alias the pointer as otherwise it may change out
336
+ // from under us by the operation of Close() from
337
+ // another thread.
338
+ QueueingBasicConsumer consumer = m_consumer ;
339
+ if ( consumer == null ) {
328
340
// Closed!
329
341
m_latestEvent = null ;
330
342
} else {
331
- m_latestEvent = ( BasicDeliverEventArgs ) m_consumer . Queue . Dequeue ( ) ;
343
+ m_latestEvent = ( BasicDeliverEventArgs ) consumer . Queue . Dequeue ( ) ;
332
344
}
333
345
} catch ( EndOfStreamException ) {
334
346
m_latestEvent = null ;
@@ -383,12 +395,16 @@ public BasicDeliverEventArgs Next()
383
395
public bool Next ( int millisecondsTimeout , out BasicDeliverEventArgs result )
384
396
{
385
397
try {
386
- if ( m_consumer == null ) {
398
+ // Alias the pointer as otherwise it may change out
399
+ // from under us by the operation of Close() from
400
+ // another thread.
401
+ QueueingBasicConsumer consumer = m_consumer ;
402
+ if ( consumer == null ) {
387
403
// Closed!
388
404
m_latestEvent = null ;
389
405
} else {
390
406
object qValue ;
391
- if ( ! m_consumer . Queue . Dequeue ( millisecondsTimeout , out qValue ) ) {
407
+ if ( ! consumer . Queue . Dequeue ( millisecondsTimeout , out qValue ) ) {
392
408
result = null ;
393
409
return false ;
394
410
}
0 commit comments