Skip to content

Commit 5abfed7

Browse files
summeroffclaude
andauthored
Fix volmeter teardown deadlock between audio thread and IPC (#1718)
OBSCallback runs while libobs holds the volmeter callback_mutex, then takes the osn global mtx (and the manager's internal_mutex via find). The teardown paths acquire those osn locks first and then take callback_mutex: - Destroy holds mtx, then obs_volmeter_remove_callback -> callback_mutex - ClearVolmeters' for_each holds internal_mutex, then obs_volmeter_remove_callback -> callback_mutex This is two AB-BA cycles (callback_mutex<->mtx, callback_mutex<->internal_mutex) that can hang on shutdown while audio is still flowing. - OBSCallback now try_locks mtx and drops the update if busy, so the audio thread never blocks on mtx while holding callback_mutex. - ClearVolmeters collects the volmeters under the manager lock, then removes callbacks and clears outside for_each, so callback_mutex is never taken while the manager lock is held. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3e3f941 commit 5abfed7

1 file changed

Lines changed: 12 additions & 3 deletions

File tree

obs-studio-server/source/osn-volmeter.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@ void osn::Volmeter::Register(ipc::server &srv)
5656

5757
void osn::Volmeter::ClearVolmeters()
5858
{
59-
Manager::GetInstance().for_each(
60-
[](const std::shared_ptr<osn::Volmeter> &volmeter) { obs_volmeter_remove_callback(volmeter->self, OBSCallback, &volmeter->id); });
59+
// Remove callbacks outside for_each: holding the manager lock across callback_mutex deadlocks OBSCallback.
60+
std::vector<std::shared_ptr<osn::Volmeter>> meters;
61+
Manager::GetInstance().for_each([&](const std::shared_ptr<osn::Volmeter> &volmeter) { meters.push_back(volmeter); });
62+
63+
for (const auto &meter : meters)
64+
obs_volmeter_remove_callback(meter->self, OBSCallback, &meter->id);
6165

6266
Manager::GetInstance().clear();
6367
}
@@ -151,7 +155,12 @@ void osn::Volmeter::Detach(void *data, const int64_t id, const std::vector<ipc::
151155
void osn::Volmeter::OBSCallback(void *param, const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
152156
const float input_peak[MAX_AUDIO_CHANNELS])
153157
{
154-
std::unique_lock<std::mutex> ulockMutex(mtx);
158+
// Runs under callback_mutex; block-waiting on mtx would deadlock teardown, so drop the frame if mtx is busy.
159+
std::unique_lock<std::mutex> ulockMutex(mtx, std::try_to_lock);
160+
if (!ulockMutex.owns_lock()) {
161+
return;
162+
}
163+
155164
auto meter = Manager::GetInstance().find(*reinterpret_cast<uint64_t *>(param));
156165
if (!meter) {
157166
return;

0 commit comments

Comments
 (0)