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
This commit is contained in:
Anghelo Carvajal 2024-06-18 13:02:18 -04:00 committed by GitHub
parent c91627740f
commit 27282afa2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 76 additions and 43 deletions

View file

@ -56,7 +56,8 @@ namespace recomp {
const ultramodern::input::callbacks_t& input_callbacks, const ultramodern::input::callbacks_t& input_callbacks,
const ultramodern::gfx_callbacks_t& gfx_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks,
const ultramodern::events::callbacks_t& events_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); void start_game(const std::u8string& game_id);

View file

@ -381,7 +381,8 @@ void recomp::start(
const ultramodern::input::callbacks_t& input_callbacks, const ultramodern::input::callbacks_t& input_callbacks,
const ultramodern::gfx_callbacks_t& gfx_callbacks_, const ultramodern::gfx_callbacks_t& gfx_callbacks_,
const ultramodern::events::callbacks_t& events_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(); recomp::check_all_stored_roms();
@ -392,7 +393,7 @@ void recomp::start(
.run_task = recomp::rsp::run_task, .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_; ultramodern::gfx_callbacks_t gfx_callbacks = gfx_callbacks_;

View file

@ -1,14 +1,10 @@
#ifndef __RSP_STUFF_HPP__ #ifndef __RSP_HPP__
#define __RSP_STUFF_HPP__ #define __RSP_HPP__
// TODO: rename
#include <cstdint> #include <cstdint>
#include "ultra64.h" #include "ultra64.h"
// TODO: Move these to ultramodern namespace?
namespace ultramodern { namespace ultramodern {
namespace rsp { namespace rsp {
struct callbacks_t { struct callbacks_t {

View file

@ -0,0 +1,29 @@
#ifndef __THREADS_HPP__
#define __THREADS_HPP__
#include <string>
#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

View file

@ -9,13 +9,15 @@
#undef MOODYCAMEL_DELETE_FUNCTION #undef MOODYCAMEL_DELETE_FUNCTION
#define MOODYCAMEL_DELETE_FUNCTION = delete #define MOODYCAMEL_DELETE_FUNCTION = delete
#include "lightweightsemaphore.h" #include "lightweightsemaphore.h"
#include "ultra64.h" #include "ultra64.h"
#include "ultramodern/error_handling.hpp" #include "ultramodern/error_handling.hpp"
#include "ultramodern/events.hpp" #include "ultramodern/events.hpp"
#include "ultramodern/input.hpp" #include "ultramodern/input.hpp"
#include "ultramodern/rsp.hpp"
#include "ultramodern/renderer_context.hpp" #include "ultramodern/renderer_context.hpp"
#include "ultramodern/rsp.hpp"
#include "ultramodern/threads.hpp"
struct UltraThreadContext { struct UltraThreadContext {
std::thread host_thread; 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 resume_thread_and_wait(RDRAM_ARG OSThread* t);
void schedule_running_thread(RDRAM_ARG PTR(OSThread) t); void schedule_running_thread(RDRAM_ARG PTR(OSThread) t);
void cleanup_thread(UltraThreadContext* thread_context); void cleanup_thread(UltraThreadContext* thread_context);
uint32_t permanent_thread_count();
uint32_t temporary_thread_count();
struct thread_terminated : std::exception {}; struct thread_terminated : std::exception {};
enum class ThreadPriority { enum class ThreadPriority {
@ -144,7 +144,8 @@ void set_callbacks(
const input::callbacks_t& input_callbacks, const input::callbacks_t& input_callbacks,
const gfx_callbacks_t& gfx_callbacks, const gfx_callbacks_t& gfx_callbacks,
const events::callbacks_t& events_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 } // namespace ultramodern

View file

@ -7,11 +7,26 @@
#include "ultramodern/ultramodern.hpp" #include "ultramodern/ultramodern.hpp"
#include "blockingconcurrentqueue.h" #include "blockingconcurrentqueue.h"
#include "ultramodern/threads.hpp"
// Native APIs only used to set thread names for easier debugging // Native APIs only used to set thread names for easier debugging
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#endif #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(); extern "C" void bootproc();
thread_local bool is_main_thread = false; thread_local bool is_main_thread = false;
@ -80,8 +95,15 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) {
// SetThreadPriority(GetCurrentThread(), nPriority); // SetThreadPriority(GetCurrentThread(), nPriority);
} }
#elif defined(__linux__) #elif defined(__linux__)
#include <sys/prctl.h>
void ultramodern::set_native_thread_name(const std::string& name) { 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) { void ultramodern::set_native_thread_priority(ThreadPriority pri) {
@ -113,15 +135,17 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) {
} }
#elif defined(__APPLE__) #elif defined(__APPLE__)
void ultramodern::set_native_thread_name(const std::string& name) { 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()); pthread_setname_np(name.c_str());
} }
void ultramodern::set_native_thread_priority(ThreadPriority pri) {} void ultramodern::set_native_thread_priority(ThreadPriority pri) {}
#endif #endif
std::atomic_int temporary_threads = 0;
std::atomic_int permanent_threads = 0;
void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) { void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) {
TO_PTR(OSThread, ultramodern::this_thread())->context->running.wait(); 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. // 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; is_game_thread = true;
// Set the thread name // 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); 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. // Signal the initialized semaphore to indicate that this thread can be started.
thread_context->initialized.signal(); 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 until the thread is marked as running.
wait_for_resumed(PASS_RDRAM thread_context); wait_for_resumed(PASS_RDRAM thread_context);
// Make sure the thread wasn't replaced or destroyed before it was started. // Make sure the thread wasn't replaced or destroyed before it was started.
if (self->context == thread_context) { if (self->context == thread_context) {
debug_printf("[Thread] Thread started: %d\n", self->id); 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. // Dispose of this thread now that it's completed or terminated.
ultramodern::cleanup_thread(thread_context); 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_) { extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {

View file

@ -7,16 +7,18 @@ void ultramodern::set_callbacks(
const audio_callbacks_t& audio_callbacks, const audio_callbacks_t& audio_callbacks,
const input::callbacks_t& input_callbacks, const input::callbacks_t& input_callbacks,
const gfx_callbacks_t& gfx_callbacks, const gfx_callbacks_t& gfx_callbacks,
const events::callbacks_t& thread_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
) { ) {
ultramodern::rsp::set_callbacks(rsp_callbacks); ultramodern::rsp::set_callbacks(rsp_callbacks);
ultramodern::renderer::set_callbacks(renderer_callbacks); ultramodern::renderer::set_callbacks(renderer_callbacks);
ultramodern::set_audio_callbacks(audio_callbacks); ultramodern::set_audio_callbacks(audio_callbacks);
ultramodern::input::set_callbacks(input_callbacks); ultramodern::input::set_callbacks(input_callbacks);
(void)gfx_callbacks; // nothing yet (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::error_handling::set_callbacks(error_handling_callbacks);
ultramodern::threads::set_callbacks(threads_callbacks);
} }
void ultramodern::preinit(RDRAM_ARG ultramodern::renderer::WindowHandle window_handle) { void ultramodern::preinit(RDRAM_ARG ultramodern::renderer::WindowHandle window_handle) {