From 3162f3b0d7b6d7e1e9e47e1a3ebf9962a4ff844d Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Tue, 27 Jan 2026 13:33:01 -1000 Subject: [PATCH 1/3] Fix shutdown deadlock --- cmd/examples/client.cpp | 8 ++++---- src/transport_picoquic.cpp | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/examples/client.cpp b/cmd/examples/client.cpp index d797c0250..de8e22379 100644 --- a/cmd/examples/client.cpp +++ b/cmd/examples/client.cpp @@ -1172,9 +1172,6 @@ main(int argc, char* argv[]) // Install a signal handlers to catch operating system signals installSignalHandlers(); - // Lock the mutex so that main can then wait on it - std::unique_lock lock(moq_example::main_mutex); - bool enable_pub{ false }; bool enable_sub{ false }; bool enable_fetch{ false }; @@ -1190,7 +1187,7 @@ main(int argc, char* argv[]) exit(-1); } - while (not stop_threads) { + while (not stop_threads && not moq_example::terminate) { if (client->GetStatus() == MyClient::Status::kReady) { SPDLOG_INFO("Connected to server"); break; @@ -1198,6 +1195,9 @@ main(int argc, char* argv[]) std::this_thread::sleep_for(std::chrono::milliseconds(200)); } + // Lock the mutex for cv.wait + std::unique_lock lock(moq_example::main_mutex); + std::thread pub_thread; std::thread sub_thread; std::thread fetch_thread; diff --git a/src/transport_picoquic.cpp b/src/transport_picoquic.cpp index fedaf886f..4662fb1ce 100644 --- a/src/transport_picoquic.cpp +++ b/src/transport_picoquic.cpp @@ -2628,6 +2628,9 @@ PicoQuicTransport::Shutdown() if (quic_network_thread_ctx_ != NULL) { SPDLOG_LOGGER_INFO(logger, "Closing transport picoquic thread"); + + // Signal thread to exit BEFORE waiting + quic_network_thread_ctx_->thread_should_close = 1; picoquic_wake_up_network_thread(quic_network_thread_ctx_); while (quic_network_thread_ctx_->thread_is_ready) { From cc3a1a02f7ef42f7f3a6cfc5822086e583496411 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Tue, 27 Jan 2026 13:52:36 -1000 Subject: [PATCH 2/3] Use atomic --- cmd/examples/client.cpp | 2 +- cmd/examples/server.cpp | 2 +- cmd/examples/signal_handler.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/examples/client.cpp b/cmd/examples/client.cpp index de8e22379..0aacfe43d 100644 --- a/cmd/examples/client.cpp +++ b/cmd/examples/client.cpp @@ -1258,7 +1258,7 @@ main(int argc, char* argv[]) } // Wait until told to terminate - moq_example::cv.wait(lock, [&]() { return moq_example::terminate; }); + moq_example::cv.wait(lock, [&]() { return moq_example::terminate.load(); }); stop_threads = true; SPDLOG_INFO("Stopping threads..."); diff --git a/cmd/examples/server.cpp b/cmd/examples/server.cpp index 79d8d4872..f4c80e249 100644 --- a/cmd/examples/server.cpp +++ b/cmd/examples/server.cpp @@ -1065,7 +1065,7 @@ main(int argc, char* argv[]) } // Wait until told to terminate - moq_example::cv.wait(lock, [&]() { return moq_example::terminate; }); + moq_example::cv.wait(lock, [&]() { return moq_example::terminate.load(); }); // Unlock the mutex lock.unlock(); diff --git a/cmd/examples/signal_handler.h b/cmd/examples/signal_handler.h index 0daa22ca6..23fd73ed4 100644 --- a/cmd/examples/signal_handler.h +++ b/cmd/examples/signal_handler.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,7 +11,7 @@ namespace moq_example { std::mutex main_mutex; // Main's mutex - bool terminate{ false }; // Termination flag + std::atomic terminate{ false }; // Termination flag std::condition_variable cv; // Main thread waits on this const char* termination_reason{ nullptr }; // Termination reason }; From 5d1d4f6ef011638501c3f525965c836cee65d696 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Tue, 27 Jan 2026 13:55:58 -1000 Subject: [PATCH 3/3] Make load explicit --- cmd/examples/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/examples/client.cpp b/cmd/examples/client.cpp index 0aacfe43d..2c99841ba 100644 --- a/cmd/examples/client.cpp +++ b/cmd/examples/client.cpp @@ -1187,7 +1187,7 @@ main(int argc, char* argv[]) exit(-1); } - while (not stop_threads && not moq_example::terminate) { + while (not stop_threads && not moq_example::terminate.load()) { if (client->GetStatus() == MyClient::Status::kReady) { SPDLOG_INFO("Connected to server"); break;