@@ -310,15 +310,23 @@ Result ConsumerImpl::handleCreateConsumer(const ClientConnectionPtr& cnx, Result
310
310
if (result == ResultOk) {
311
311
LOG_INFO (getName () << " Created consumer on broker " << cnx->cnxString ());
312
312
{
313
- Lock lock (mutex_);
313
+ Lock mutexLock (mutex_);
314
314
setCnx (cnx);
315
315
incomingMessages_.clear ();
316
316
possibleSendToDeadLetterTopicMessages_.clear ();
317
317
state_ = Ready;
318
318
backoff_.reset ();
319
- // Complicated logic since we don't have a isLocked() function for mutex
320
- if (waitingForZeroQueueSizeMessage) {
321
- sendFlowPermitsToBroker (cnx, 1 );
319
+ if (!messageListener_ && config_.getReceiverQueueSize () == 0 ) {
320
+ // Complicated logic since we don't have a isLocked() function for mutex
321
+ if (waitingForZeroQueueSizeMessage) {
322
+ sendFlowPermitsToBroker (cnx, 1 );
323
+ }
324
+ // Note that the order of lock acquisition must be mutex_ -> pendingReceiveMutex_,
325
+ // otherwise a deadlock will occur.
326
+ Lock pendingReceiveMutexLock (pendingReceiveMutex_);
327
+ if (!pendingReceives_.empty ()) {
328
+ sendFlowPermitsToBroker (cnx, pendingReceives_.size ());
329
+ }
322
330
}
323
331
availablePermits_ = 0 ;
324
332
}
@@ -915,7 +923,6 @@ Result ConsumerImpl::fetchSingleMessageFromBroker(Message& msg) {
915
923
}
916
924
917
925
// Using RAII for locking
918
- ClientConnectionPtr currentCnx = getCnx ().lock ();
919
926
Lock lock (mutexForReceiveWithZeroQueueSize);
920
927
921
928
// Just being cautious
@@ -924,9 +931,18 @@ Result ConsumerImpl::fetchSingleMessageFromBroker(Message& msg) {
924
931
getName () << " The incoming message queue should never be greater than 0 when Queue size is 0" );
925
932
incomingMessages_.clear ();
926
933
}
927
- waitingForZeroQueueSizeMessage = true ;
928
934
929
- sendFlowPermitsToBroker (currentCnx, 1 );
935
+ {
936
+ // Lock mutex_ to prevent a race condition with handleCreateConsumer.
937
+ // If handleCreateConsumer is executed after setting waitingForZeroQueueSizeMessage to true and
938
+ // before calling sendFlowPermitsToBroker, the result may be that a flow permit is sent twice.
939
+ Lock lock (mutex_);
940
+ waitingForZeroQueueSizeMessage = true ;
941
+ // If connection_ is nullptr, sendFlowPermitsToBroker does nothing.
942
+ // In other words, a flow permit will not be sent until setCnx(cnx) is executed in
943
+ // handleCreateConsumer.
944
+ sendFlowPermitsToBroker (getCnx ().lock (), 1 );
945
+ }
930
946
931
947
while (true ) {
932
948
if (!incomingMessages_.pop (msg)) {
@@ -939,6 +955,7 @@ Result ConsumerImpl::fetchSingleMessageFromBroker(Message& msg) {
939
955
Lock localLock (mutex_);
940
956
// if message received due to an old flow - discard it and wait for the message from the
941
957
// latest flow command
958
+ ClientConnectionPtr currentCnx = getCnx ().lock ();
942
959
if (msg.impl_ ->cnx_ == currentCnx.get ()) {
943
960
waitingForZeroQueueSizeMessage = false ;
944
961
// Can't use break here else it may trigger a race with connection opened.
@@ -966,19 +983,42 @@ void ConsumerImpl::receiveAsync(ReceiveCallback callback) {
966
983
return ;
967
984
}
968
985
969
- Lock lock (pendingReceiveMutex_);
986
+ if (messageListener_) {
987
+ LOG_ERROR (getName () << " Can not receive when a listener has been set" );
988
+ callback (ResultInvalidConfiguration, msg);
989
+ return ;
990
+ }
991
+
992
+ Lock mutexlock (mutex_, std::defer_lock);
993
+ if (config_.getReceiverQueueSize () == 0 ) {
994
+ // Lock mutex_ to prevent a race condition with handleCreateConsumer.
995
+ // If handleCreateConsumer is executed after pushing the callback to pendingReceives_ and
996
+ // before calling sendFlowPermitsToBroker, the result may be that a flow permit is sent twice.
997
+ // Note that the order of lock acquisition must be mutex_ -> pendingReceiveMutex_,
998
+ // otherwise a deadlock will occur.
999
+ mutexlock.lock ();
1000
+ }
1001
+
1002
+ Lock pendingReceiveMutexLock (pendingReceiveMutex_);
970
1003
if (incomingMessages_.pop (msg, std::chrono::milliseconds (0 ))) {
971
- lock.unlock ();
1004
+ pendingReceiveMutexLock.unlock ();
1005
+ if (config_.getReceiverQueueSize () == 0 ) {
1006
+ mutexlock.unlock ();
1007
+ }
972
1008
messageProcessed (msg);
973
1009
msg = interceptors_->beforeConsume (Consumer (shared_from_this ()), msg);
974
1010
callback (ResultOk, msg);
1011
+ } else if (config_.getReceiverQueueSize () == 0 ) {
1012
+ pendingReceives_.push (callback);
1013
+ // If connection_ is nullptr, sendFlowPermitsToBroker does nothing.
1014
+ // In other words, a flow permit will not be sent until setCnx(cnx) is executed in
1015
+ // handleCreateConsumer.
1016
+ sendFlowPermitsToBroker (getCnx ().lock (), 1 );
1017
+ pendingReceiveMutexLock.unlock ();
1018
+ mutexlock.unlock ();
975
1019
} else {
976
1020
pendingReceives_.push (callback);
977
- lock.unlock ();
978
-
979
- if (config_.getReceiverQueueSize () == 0 ) {
980
- sendFlowPermitsToBroker (getCnx ().lock (), 1 );
981
- }
1021
+ pendingReceiveMutexLock.unlock ();
982
1022
}
983
1023
}
984
1024
0 commit comments