From 8dcbbc3873ef97d166976e6ccccf7bdaf4e35b62 Mon Sep 17 00:00:00 2001
From: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
Date: Mon, 6 Sep 2021 16:59:44 -0400
Subject: [PATCH] audio layer: prevent a deadlock

A deadlock is caused by a lock-order-inversion of the private
mutex from pulse-audio thread loop, and the callMutex_ mutex
from the Call class. It occurs when concurrently calling
writeToSpeaker() and onNegoDone() callbacks.
The deadlock is prevented by avoid to a access Call data from
the pulse-audio thread.

Gitlab: #623

Change-Id: I91d936b37528db3de24e93b0d49d686f5ca11813
---
 src/manager.cpp                |  5 +++++
 src/media/audio/audiolayer.cpp |  8 ++------
 src/media/audio/audiolayer.h   | 17 ++++++++++++++---
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/src/manager.cpp b/src/manager.cpp
index 73ef2833c2..7589759b91 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -615,6 +615,9 @@ void
 Manager::ManagerPimpl::addWaitingCall(const std::string& id)
 {
     std::lock_guard<std::mutex> m(waitingCallsMutex_);
+    // Enable incoming call beep if needed.
+    if (audiodriver_ and waitingCalls_.empty() and not currentCall_.empty())
+        audiodriver_->playIncomingCallNotification(true);
     waitingCalls_.insert(id);
 }
 
@@ -623,6 +626,8 @@ Manager::ManagerPimpl::removeWaitingCall(const std::string& id)
 {
     std::lock_guard<std::mutex> m(waitingCallsMutex_);
     waitingCalls_.erase(id);
+    if (audiodriver_ and waitingCalls_.empty())
+        audiodriver_->playIncomingCallNotification(false);
 }
 
 void
diff --git a/src/media/audio/audiolayer.cpp b/src/media/audio/audiolayer.cpp
index 3f7e3f874b..dd86480235 100644
--- a/src/media/audio/audiolayer.cpp
+++ b/src/media/audio/audiolayer.cpp
@@ -164,21 +164,17 @@ AudioLayer::putUrgent(AudioBuffer& buffer)
 void
 AudioLayer::notifyIncomingCall()
 {
-    if (!Manager::instance().incomingCallsWaiting())
+    if (not playIncomingCallBeep_)
         return;
 
     auto now = std::chrono::system_clock::now();
 
     // Notify maximum once every 5 seconds
-    if ((now - lastNotificationTime_) < std::chrono::seconds(5))
+    if (now < lastNotificationTime_ + std::chrono::seconds(5))
         return;
 
     lastNotificationTime_ = now;
 
-    // Enable notification only if more than one call
-    if (!Manager::instance().hasCurrentCall())
-        return;
-
     Tone tone("440/160", getSampleRate());
     size_t nbSample = tone.getSize();
     AudioBuffer buf(nbSample, AudioFormat::MONO());
diff --git a/src/media/audio/audiolayer.h b/src/media/audio/audiolayer.h
index 79ad74d36b..86562bee8c 100644
--- a/src/media/audio/audiolayer.h
+++ b/src/media/audio/audiolayer.h
@@ -53,7 +53,7 @@ typedef struct SpeexEchoState_ SpeexEchoState;
 #define COREAUDIO_API_STR  "coreaudio"
 #define PORTAUDIO_API_STR  "portaudio"
 
-#define PCM_DEFAULT     "default" // Default ALSA plugin
+#define PCM_DEFAULT     "default"     // Default ALSA plugin
 #define PCM_DSNOOP      "plug:dsnoop" // Alsa plugin for microphone sharing
 #define PCM_DMIX_DSNOOP "dmix/dsnoop" // Audio profile using Alsa dmix/dsnoop
 
@@ -117,6 +117,12 @@ public:
      */
     void putUrgent(AudioBuffer& buffer);
 
+    /**
+     * Start/Stop playing the incoming call notification sound (beep)
+     * while playing back audio (typically during an ongoing call).
+     */
+    void playIncomingCallNotification(bool play) { playIncomingCallBeep_.exchange(play); }
+
     /**
      * Flush main buffer
      */
@@ -179,7 +185,7 @@ public:
     AudioFormat getFormat() const { return audioFormat_; }
 
     /**
-     * Emit an audio notification on incoming calls
+     * Emit an audio notification (beep) on incoming calls
      */
     void notifyIncomingCall();
 
@@ -295,10 +301,15 @@ protected:
 private:
     void checkAEC();
 
+    // Set to "true" to play the incoming call notification (beep)
+    // when the playback is on (typically when there is already an
+    // active call).
+    std::atomic_bool playIncomingCallBeep_ {false};
     /**
      * Time of the last incoming call notification
      */
-    std::chrono::system_clock::time_point lastNotificationTime_;
+    std::chrono::system_clock::time_point lastNotificationTime_ {
+        std::chrono::system_clock::time_point::min()};
 };
 
 } // namespace jami
-- 
GitLab