From 27282afa2b6e9f1749463f0bf6ea2761cc56769b Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Tue, 18 Jun 2024 13:02:18 -0400 Subject: [PATCH] Remove "permanent" and "temporary" threads tagging and add a way to name game threads (#45) * System to specify thread types by the game * Register threads callbacks * Remove temporary and permanent threads * Fix `pthread_setname_np` not liking names longer than 16 bytes * Singular * Rename events_callbacks arg * Use `prctl` instead of `pthread_setname_np` for naming a thread * Fix typo --- librecomp/include/librecomp/game.hpp | 3 +- librecomp/src/recomp.cpp | 5 +- ultramodern/include/ultramodern/rsp.hpp | 8 +-- ultramodern/include/ultramodern/threads.hpp | 29 ++++++++++ .../include/ultramodern/ultramodern.hpp | 9 +-- ultramodern/src/threads.cpp | 57 ++++++++++--------- ultramodern/src/ultrainit.cpp | 8 ++- 7 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 ultramodern/include/ultramodern/threads.hpp diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index 0e108a9..4c2b304 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -56,7 +56,8 @@ namespace recomp { const ultramodern::input::callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks, const ultramodern::events::callbacks_t& events_callbacks, - const ultramodern::error_handling::callbacks_t& error_handling_callbacks_ + const ultramodern::error_handling::callbacks_t& error_handling_callbacks, + const ultramodern::threads::callbacks_t& threads_callbacks ); void start_game(const std::u8string& game_id); diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index ce50a81..75f8a05 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -381,7 +381,8 @@ void recomp::start( const ultramodern::input::callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks_, const ultramodern::events::callbacks_t& events_callbacks, - const ultramodern::error_handling::callbacks_t& error_handling_callbacks_ + const ultramodern::error_handling::callbacks_t& error_handling_callbacks, + const ultramodern::threads::callbacks_t& threads_callbacks ) { recomp::check_all_stored_roms(); @@ -392,7 +393,7 @@ void recomp::start( .run_task = recomp::rsp::run_task, }; - ultramodern::set_callbacks(ultramodern_rsp_callbacks, renderer_callbacks, audio_callbacks, input_callbacks, gfx_callbacks_, events_callbacks, error_handling_callbacks_); + ultramodern::set_callbacks(ultramodern_rsp_callbacks, renderer_callbacks, audio_callbacks, input_callbacks, gfx_callbacks_, events_callbacks, error_handling_callbacks, threads_callbacks); ultramodern::gfx_callbacks_t gfx_callbacks = gfx_callbacks_; diff --git a/ultramodern/include/ultramodern/rsp.hpp b/ultramodern/include/ultramodern/rsp.hpp index be1a85c..dda1b08 100644 --- a/ultramodern/include/ultramodern/rsp.hpp +++ b/ultramodern/include/ultramodern/rsp.hpp @@ -1,14 +1,10 @@ -#ifndef __RSP_STUFF_HPP__ -#define __RSP_STUFF_HPP__ - -// TODO: rename +#ifndef __RSP_HPP__ +#define __RSP_HPP__ #include #include "ultra64.h" -// TODO: Move these to ultramodern namespace? - namespace ultramodern { namespace rsp { struct callbacks_t { diff --git a/ultramodern/include/ultramodern/threads.hpp b/ultramodern/include/ultramodern/threads.hpp new file mode 100644 index 0000000..0bf0f98 --- /dev/null +++ b/ultramodern/include/ultramodern/threads.hpp @@ -0,0 +1,29 @@ +#ifndef __THREADS_HPP__ +#define __THREADS_HPP__ + +#include + +#include "ultra64.h" + +namespace ultramodern { + namespace threads { + struct callbacks_t { + using get_game_thread_name_t = std::string(const OSThread* t); + + /** + * Allows to specifying a custom name for each thread. Mainly for debugging purposes. + * + * For maximum cross-platform compatibility the returned name should be at most 15 bytes long (16 bytes including the null terminator). + * + * If this function is not provided then the thread id will be used as the name of the thread. + */ + get_game_thread_name_t *get_game_thread_name; + }; + + void set_callbacks(const callbacks_t& callbacks); + + std::string get_game_thread_name(const OSThread* t); + } +} + +#endif diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index 8dbccdc..b144eaa 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -9,13 +9,15 @@ #undef MOODYCAMEL_DELETE_FUNCTION #define MOODYCAMEL_DELETE_FUNCTION = delete #include "lightweightsemaphore.h" + #include "ultra64.h" #include "ultramodern/error_handling.hpp" #include "ultramodern/events.hpp" #include "ultramodern/input.hpp" -#include "ultramodern/rsp.hpp" #include "ultramodern/renderer_context.hpp" +#include "ultramodern/rsp.hpp" +#include "ultramodern/threads.hpp" struct UltraThreadContext { std::thread host_thread; @@ -58,8 +60,6 @@ void run_next_thread_and_wait(RDRAM_ARG1); void resume_thread_and_wait(RDRAM_ARG OSThread* t); void schedule_running_thread(RDRAM_ARG PTR(OSThread) t); void cleanup_thread(UltraThreadContext* thread_context); -uint32_t permanent_thread_count(); -uint32_t temporary_thread_count(); struct thread_terminated : std::exception {}; enum class ThreadPriority { @@ -144,7 +144,8 @@ void set_callbacks( const input::callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, const events::callbacks_t& events_callbacks, - const error_handling::callbacks_t& error_handling_callbacks + const error_handling::callbacks_t& error_handling_callbacks, + const threads::callbacks_t& threads_callbacks ); } // namespace ultramodern diff --git a/ultramodern/src/threads.cpp b/ultramodern/src/threads.cpp index adb898a..3c7df50 100644 --- a/ultramodern/src/threads.cpp +++ b/ultramodern/src/threads.cpp @@ -7,11 +7,26 @@ #include "ultramodern/ultramodern.hpp" #include "blockingconcurrentqueue.h" +#include "ultramodern/threads.hpp" + // Native APIs only used to set thread names for easier debugging #ifdef _WIN32 #include #endif +static ultramodern::threads::callbacks_t threads_callbacks; + +void ultramodern::threads::set_callbacks(const callbacks_t& callbacks) { + threads_callbacks = callbacks; +} + +std::string ultramodern::threads::get_game_thread_name(const OSThread* t) { + if (threads_callbacks.get_game_thread_name == nullptr) { + return "Game Thread " + std::to_string(t->id); + } + return threads_callbacks.get_game_thread_name(t); +} + extern "C" void bootproc(); thread_local bool is_main_thread = false; @@ -80,8 +95,15 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) { // SetThreadPriority(GetCurrentThread(), nPriority); } #elif defined(__linux__) +#include + void ultramodern::set_native_thread_name(const std::string& name) { - pthread_setname_np(pthread_self(), name.c_str()); + if (name.length() > 15) { + // Linux only accepts up to 16 characters including the null terminator for a thread name. + debug_printf("[Thread] The thread name '%s' will be truncated to 15 characters", name.c_str()); + } + + prctl(PR_SET_NAME, name.c_str()); } void ultramodern::set_native_thread_priority(ThreadPriority pri) { @@ -113,15 +135,17 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) { } #elif defined(__APPLE__) void ultramodern::set_native_thread_name(const std::string& name) { + if (name.length() > 15) { + // Macs seem to only accept up to 16 characters including the null terminator for a thread name. + debug_printf("[Thread] The thread name '%s' will be truncated to 15 characters", name.c_str()); + } + pthread_setname_np(name.c_str()); } void ultramodern::set_native_thread_priority(ThreadPriority pri) {} #endif -std::atomic_int temporary_threads = 0; -std::atomic_int permanent_threads = 0; - void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) { TO_PTR(OSThread, ultramodern::this_thread())->context->running.wait(); // If this thread's context was replaced by another thread or deleted, destroy it again from its own context. @@ -165,17 +189,9 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry is_game_thread = true; // Set the thread name - ultramodern::set_native_thread_name("Game Thread " + std::to_string(self->id)); + ultramodern::set_native_thread_name(ultramodern::threads::get_game_thread_name(self)); ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::High); - // TODO fix these being hardcoded (this is only used for quicksaving) - if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom - temporary_threads.fetch_add(1); - } - else if (self->id != 1 && self->id != 2) { // ignore idle and fault - permanent_threads.fetch_add(1); - } - // Signal the initialized semaphore to indicate that this thread can be started. thread_context->initialized.signal(); @@ -183,7 +199,7 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry // Wait until the thread is marked as running. wait_for_resumed(PASS_RDRAM thread_context); - + // Make sure the thread wasn't replaced or destroyed before it was started. if (self->context == thread_context) { debug_printf("[Thread] Thread started: %d\n", self->id); @@ -206,19 +222,6 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry // Dispose of this thread now that it's completed or terminated. ultramodern::cleanup_thread(thread_context); - - // TODO fix these being hardcoded (this is only used for quicksaving) - if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom - temporary_threads.fetch_sub(1); - } -} - -uint32_t ultramodern::permanent_thread_count() { - return permanent_threads.load(); -} - -uint32_t ultramodern::temporary_thread_count() { - return temporary_threads.load(); } extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) { diff --git a/ultramodern/src/ultrainit.cpp b/ultramodern/src/ultrainit.cpp index fe11115..ca1556c 100644 --- a/ultramodern/src/ultrainit.cpp +++ b/ultramodern/src/ultrainit.cpp @@ -7,16 +7,18 @@ void ultramodern::set_callbacks( const audio_callbacks_t& audio_callbacks, const input::callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, - const events::callbacks_t& thread_callbacks, - const error_handling::callbacks_t& error_handling_callbacks + const events::callbacks_t& events_callbacks, + const error_handling::callbacks_t& error_handling_callbacks, + const threads::callbacks_t& threads_callbacks ) { ultramodern::rsp::set_callbacks(rsp_callbacks); ultramodern::renderer::set_callbacks(renderer_callbacks); ultramodern::set_audio_callbacks(audio_callbacks); ultramodern::input::set_callbacks(input_callbacks); (void)gfx_callbacks; // nothing yet - ultramodern::events::set_callbacks(thread_callbacks); + ultramodern::events::set_callbacks(events_callbacks); ultramodern::error_handling::set_callbacks(error_handling_callbacks); + ultramodern::threads::set_callbacks(threads_callbacks); } void ultramodern::preinit(RDRAM_ARG ultramodern::renderer::WindowHandle window_handle) {