@@ -310,15 +310,23 @@ Result ConsumerImpl::handleCreateConsumer(const ClientConnectionPtr& cnx, Result
310310 if (result == ResultOk) {
311311 LOG_INFO (getName () << " Created consumer on broker " << cnx->cnxString ());
312312 {
313- Lock lock (mutex_);
313+ Lock mutexLock (mutex_);
314314 setCnx (cnx);
315315 incomingMessages_.clear ();
316316 possibleSendToDeadLetterTopicMessages_.clear ();
317317 state_ = Ready;
318318 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+ }
322330 }
323331 availablePermits_ = 0 ;
324332 }
@@ -915,7 +923,6 @@ Result ConsumerImpl::fetchSingleMessageFromBroker(Message& msg) {
915923 }
916924
917925 // Using RAII for locking
918- ClientConnectionPtr currentCnx = getCnx ().lock ();
919926 Lock lock (mutexForReceiveWithZeroQueueSize);
920927
921928 // Just being cautious
@@ -924,9 +931,18 @@ Result ConsumerImpl::fetchSingleMessageFromBroker(Message& msg) {
924931 getName () << " The incoming message queue should never be greater than 0 when Queue size is 0" );
925932 incomingMessages_.clear ();
926933 }
927- waitingForZeroQueueSizeMessage = true ;
928934
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+ }
930946
931947 while (true ) {
932948 if (!incomingMessages_.pop (msg)) {
@@ -939,6 +955,7 @@ Result ConsumerImpl::fetchSingleMessageFromBroker(Message& msg) {
939955 Lock localLock (mutex_);
940956 // if message received due to an old flow - discard it and wait for the message from the
941957 // latest flow command
958+ ClientConnectionPtr currentCnx = getCnx ().lock ();
942959 if (msg.impl_ ->cnx_ == currentCnx.get ()) {
943960 waitingForZeroQueueSizeMessage = false ;
944961 // Can't use break here else it may trigger a race with connection opened.
@@ -966,19 +983,42 @@ void ConsumerImpl::receiveAsync(ReceiveCallback callback) {
966983 return ;
967984 }
968985
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_);
9701003 if (incomingMessages_.pop (msg, std::chrono::milliseconds (0 ))) {
971- lock.unlock ();
1004+ pendingReceiveMutexLock.unlock ();
1005+ if (config_.getReceiverQueueSize () == 0 ) {
1006+ mutexlock.unlock ();
1007+ }
9721008 messageProcessed (msg);
9731009 msg = interceptors_->beforeConsume (Consumer (shared_from_this ()), msg);
9741010 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 ();
9751019 } else {
9761020 pendingReceives_.push (callback);
977- lock.unlock ();
978-
979- if (config_.getReceiverQueueSize () == 0 ) {
980- sendFlowPermitsToBroker (getCnx ().lock (), 1 );
981- }
1021+ pendingReceiveMutexLock.unlock ();
9821022 }
9831023}
9841024
0 commit comments