diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 850689d6..2ce141e1 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -993,6 +993,7 @@ void recomp::start(const recomp::Configuration& cfg) { game_thread.join(); ultramodern::join_event_threads(); + ultramodern::join_timer_thread(); ultramodern::join_thread_cleaner_thread(); ultramodern::join_saving_thread(); diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index 46aea61b..292f449b 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -146,6 +146,7 @@ struct gfx_callbacks_t { bool is_game_started(); void quit(); void join_event_threads(); +void join_timer_thread(); void join_thread_cleaner_thread(); void join_saving_thread(); diff --git a/ultramodern/src/mesgqueue.cpp b/ultramodern/src/mesgqueue.cpp index 9944a02f..f7c14513 100644 --- a/ultramodern/src/mesgqueue.cpp +++ b/ultramodern/src/mesgqueue.cpp @@ -13,7 +13,10 @@ struct QueuedMessage { bool requeue_if_blocked; }; -static moodycamel::BlockingConcurrentQueue external_messages {}; +static moodycamel::BlockingConcurrentQueue& external_messages_queue() { + static auto *queue = new moodycamel::BlockingConcurrentQueue(); + return *queue; +} std::bitset<32> requeue_enabled; void ultramodern::set_message_queue_control(const ultramodern::MessageQueueControl& mqc) { @@ -28,11 +31,11 @@ void ultramodern::set_message_queue_control(const ultramodern::MessageQueueContr } void ultramodern::enqueue_external_message_src(PTR(OSMesgQueue) mq, OSMesg msg, bool jam, EventMessageSource src) { - external_messages.enqueue({mq, msg, jam, requeue_enabled[static_cast(src)]}); + external_messages_queue().enqueue({mq, msg, jam, requeue_enabled[static_cast(src)]}); } void ultramodern::enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam, bool requeue_if_blocked) { - external_messages.enqueue({mq, msg, jam, requeue_if_blocked}); + external_messages_queue().enqueue({mq, msg, jam, requeue_if_blocked}); } bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block); @@ -40,29 +43,29 @@ bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block); void dequeue_external_messages(RDRAM_ARG1) { QueuedMessage to_send; std::vector requeued_messages{}; - while (external_messages.try_dequeue(to_send)) { + while (external_messages_queue().try_dequeue(to_send)) { if (!do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false) && to_send.requeue_if_blocked) { requeued_messages.push_back(to_send); } } for (QueuedMessage& cur_mesg : requeued_messages) { - external_messages.enqueue(cur_mesg); + external_messages_queue().enqueue(cur_mesg); } } void ultramodern::wait_for_external_message(RDRAM_ARG1) { QueuedMessage to_send; - external_messages.wait_dequeue(to_send); + external_messages_queue().wait_dequeue(to_send); if (!do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false) && to_send.requeue_if_blocked) { - external_messages.enqueue(to_send); + external_messages_queue().enqueue(to_send); } } void ultramodern::wait_for_external_message_timed(RDRAM_ARG u32 millis) { QueuedMessage to_send; - if (external_messages.wait_dequeue_timed(to_send, std::chrono::milliseconds{millis})) { + if (external_messages_queue().wait_dequeue_timed(to_send, std::chrono::milliseconds{millis})) { if (!do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false) && to_send.requeue_if_blocked) { - external_messages.enqueue(to_send); + external_messages_queue().enqueue(to_send); } } } diff --git a/ultramodern/src/timer.cpp b/ultramodern/src/timer.cpp index ee26c453..662c9f59 100644 --- a/ultramodern/src/timer.cpp +++ b/ultramodern/src/timer.cpp @@ -37,7 +37,9 @@ struct RemoveTimerAction { PTR(OSTimer) timer; }; -using Action = std::variant; +struct StopTimerThreadAction {}; + +using Action = std::variant; struct { std::thread thread; @@ -87,6 +89,8 @@ void timer_thread(RDRAM_ARG1) { // Ordered set of timers that are currently active std::set active_timers{timer_sort}; + bool stop_requested = false; + // Lambda to process a timer action to handle adding and removing timers auto process_timer_action = [&](const Action& action) { // Determine the action type and act on it @@ -94,20 +98,30 @@ void timer_thread(RDRAM_ARG1) { active_timers.insert(add_action->timer); } else if (const auto* remove_action = std::get_if(&action)) { active_timers.erase(remove_action->timer); + } else if (std::get_if(&action) != nullptr) { + stop_requested = true; } }; - while (true) { + while (!stop_requested) { // Empty the action queue Action cur_action; while (timer_context.action_queue.try_dequeue(cur_action)) { process_timer_action(cur_action); } + if (stop_requested) { + break; + } + // If there's no timer to act on, wait for one to come in from the action queue while (active_timers.empty()) { timer_context.action_queue.wait_dequeue(cur_action); process_timer_action(cur_action); + + if (stop_requested) { + return; + } } // Get the timer that's closest to running out @@ -142,7 +156,13 @@ void timer_thread(RDRAM_ARG1) { void ultramodern::init_timers(RDRAM_ARG1) { timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 }; - timer_context.thread.detach(); +} + +void ultramodern::join_timer_thread() { + if (timer_context.thread.joinable()) { + timer_context.action_queue.enqueue(StopTimerThreadAction{}); + timer_context.thread.join(); + } } uint32_t ultramodern::get_speed_multiplier() {