Move every thread onto rthreads and add a lock-free parallel_for

No std::thread, std::async, std::future or std::promise is left. Each
thread is an sthread with a plain entry point: Common::ThreadEntry<&C::F>
turns a member function into one, Common::JoinThread joins and clears a
handle, and Common::StartDetachedThread covers the fire-and-forget users.

Common/ParallelFor.{h,c} (C89) runs fn(ctx, i) for i in [0, count) on a
persistent pool of one worker per extra core plus the caller. Jobs are
claimed with an atomic counter, workers park on an eventcount between
batches, and the batch hand-off is a seq_cst users/active pair, so there
is no lock anywhere. A caller that finds the pool busy runs its batch
serially instead of waiting. retro_deinit joins the pool.

- VolumeWii::HashGroup spawned 64 threads per 2 MiB group and
  EncryptGroup another one per core; both are now parallel_for batches
  on the pool. The H1/H2 tail is a few small hashes and runs inline.
- VolumeVerifier spawned up to five std::async threads per chunk; the
  chunk's tasks now go to one persistent worker that fans them out with
  parallel_for, still overlapping the next read. The Redump lookup is an
  sthread joined in Finish or the destructor.
- The dual-core "GPU thread" in Core only set a thread_local on itself
  and exited, since the frontend runs the GPU loop. It is gone, along
  with the non-libretro branches of Core.cpp.
- The emu thread is started through Core::StartEmuThread.
- CPU core counts come from cpu_features_get_core_amount().
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-11-05 13:00:34 +01:00
1 parent 8c7aa58f6b
commit 767c898df8
52 files changed
+771 -451

No files matched your search

+2
View File
@@ -1108,6 +1108,8 @@ DOLPHIN_SOURCES_C :=
DOLPHIN_SOURCES_CXX :=
DOLPHIN_SOURCES_ASM :=
DOLPHIN_SOURCES_OBJCXX :=
DOLPHIN_SOURCES_C += \
$(CORE_DIR)/Source/Core/Common/ParallelFor.c
DOLPHIN_SOURCES_CXX += \
$(CORE_DIR)/Source/Core/AudioCommon/AudioCommon.cpp \
$(CORE_DIR)/Source/Core/AudioCommon/Mixer.cpp \
+4 -2
View File
@@ -7,7 +7,7 @@
#include <fstream>
#include <sstream>
#include <string>
#include <thread>
#include <algorithm>
#ifdef __APPLE__
#include <sys/sysctl.h>
@@ -41,6 +41,8 @@
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <features/features_cpu.h>
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/StringUtil.h"
@@ -228,7 +230,7 @@ void CPUInfo::Detect()
bFMA = true;
bFlushToZero = true;
num_cores = std::max(static_cast<int>(std::thread::hardware_concurrency()), 1);
num_cores = std::max(static_cast<int>(cpu_features_get_core_amount()), 1);
#ifdef __APPLE__
SysctlByName(&model_name, "machdep.cpu.brand_string");
+221
View File
@@ -0,0 +1,221 @@
/* Copyright 2026 Dolphin Emulator Project
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "Common/ParallelFor.h"
#include <stdlib.h>
#include <features/features_cpu.h>
#include <retro_atomic.h>
#include <rthreads/retro_eventcount.h>
#include <rthreads/rthreads.h>
/* Never more workers than this, whatever the core count. */
#define PARALLEL_FOR_MAX_WORKERS 63
/* One batch at a time, owned by whichever caller won m_owner.
*
* A worker touches the batch only between raising m_users and lowering it
* again, and only if it saw m_active set in between. The caller clears
* m_active once every index is claimed and then waits for m_users to drop
* to zero; both sides use seq_cst operations, so a worker either is counted
* in m_users or sees m_active clear. Once m_users reads zero no worker can
* still be inside the batch, and the caller is free to return and to rewrite
* the batch fields for the next call. */
static struct
{
sthread_t **workers;
unsigned worker_count;
int started;
/* Batch, written only by the owner while no worker is inside it. */
parallel_for_fn fn;
void *ctx;
size_t count;
retro_atomic_size_t next;
retro_atomic_int_t owner;
retro_atomic_int_t active;
retro_atomic_int_t users;
retro_atomic_int_t generation;
retro_atomic_int_t stop;
retro_eventcount_t wake;
retro_eventcount_t idle;
} s_pool;
static void parallel_for_run_jobs(void)
{
for (;;)
{
const size_t i = retro_atomic_fetch_add_size(&s_pool.next, 1);
if (i >= s_pool.count)
return;
s_pool.fn(s_pool.ctx, i);
}
}
static void parallel_for_worker(void *data)
{
int seen = 0;
(void)data;
sthread_setname("parallel_for");
for (;;)
{
int gen;
const int key = retro_eventcount_prepare_wait(&s_pool.wake);
if (retro_atomic_load_acquire_int(&s_pool.stop))
{
retro_eventcount_cancel_wait(&s_pool.wake);
return;
}
gen = retro_atomic_load_acquire_int(&s_pool.generation);
if (gen == seen)
{
retro_eventcount_commit_wait(&s_pool.wake, key);
continue;
}
retro_eventcount_cancel_wait(&s_pool.wake);
seen = gen;
retro_atomic_fetch_add_seq_cst_int(&s_pool.users, 1);
if (retro_atomic_load_seq_cst_int(&s_pool.active))
parallel_for_run_jobs();
if (retro_atomic_fetch_add_seq_cst_int(&s_pool.users, -1) == 1)
retro_eventcount_notify(&s_pool.idle);
}
}
/* Called by the owner. */
static void parallel_for_start(void)
{
unsigned cores = cpu_features_get_core_amount();
unsigned i;
s_pool.started = 1;
if (cores <= 1)
return;
if (cores - 1 > PARALLEL_FOR_MAX_WORKERS)
cores = PARALLEL_FOR_MAX_WORKERS + 1;
s_pool.workers = (sthread_t**)calloc(cores - 1, sizeof(*s_pool.workers));
if (!s_pool.workers)
return;
retro_atomic_int_init(&s_pool.active, 0);
retro_atomic_int_init(&s_pool.users, 0);
retro_atomic_int_init(&s_pool.generation, 0);
retro_atomic_int_init(&s_pool.stop, 0);
retro_atomic_size_init(&s_pool.next, 0);
if (!retro_eventcount_init(&s_pool.wake))
goto fail_wake;
if (!retro_eventcount_init(&s_pool.idle))
goto fail_idle;
for (i = 0; i < cores - 1; i++)
{
s_pool.workers[i] = sthread_create(parallel_for_worker, NULL);
if (!s_pool.workers[i])
break;
}
s_pool.worker_count = i;
if (i)
return;
retro_eventcount_free(&s_pool.idle);
fail_idle:
retro_eventcount_free(&s_pool.wake);
fail_wake:
free(s_pool.workers);
s_pool.workers = NULL;
}
static void parallel_for_serial(size_t count, parallel_for_fn fn, void *ctx)
{
size_t i;
for (i = 0; i < count; i++)
fn(ctx, i);
}
void parallel_for(size_t count, parallel_for_fn fn, void *ctx)
{
if (count < 2 || !retro_atomic_cas_int(&s_pool.owner, 0, 1))
{
parallel_for_serial(count, fn, ctx);
return;
}
if (!s_pool.started)
parallel_for_start();
if (!s_pool.worker_count)
{
parallel_for_serial(count, fn, ctx);
retro_atomic_store_release_int(&s_pool.owner, 0);
return;
}
s_pool.fn = fn;
s_pool.ctx = ctx;
s_pool.count = count;
retro_atomic_store_relaxed_size(&s_pool.next, 0);
retro_atomic_fetch_add_seq_cst_int(&s_pool.active, 1);
retro_atomic_fetch_add_int(&s_pool.generation, 1);
retro_eventcount_notify(&s_pool.wake);
parallel_for_run_jobs();
/* Every index is claimed; wait for the workers still running one. */
retro_atomic_fetch_add_seq_cst_int(&s_pool.active, -1);
while (retro_atomic_load_seq_cst_int(&s_pool.users))
{
const int key = retro_eventcount_prepare_wait(&s_pool.idle);
if (!retro_atomic_load_seq_cst_int(&s_pool.users))
{
retro_eventcount_cancel_wait(&s_pool.idle);
break;
}
retro_eventcount_commit_wait(&s_pool.idle, key);
}
retro_atomic_store_release_int(&s_pool.owner, 0);
}
unsigned parallel_for_threads(void)
{
unsigned cores = cpu_features_get_core_amount();
if (cores < 1)
return 1;
if (cores > PARALLEL_FOR_MAX_WORKERS + 1)
return PARALLEL_FOR_MAX_WORKERS + 1;
return cores;
}
void parallel_for_deinit(void)
{
unsigned i;
while (!retro_atomic_cas_int(&s_pool.owner, 0, 1))
sthread_yield();
if (s_pool.worker_count)
{
retro_atomic_store_release_int(&s_pool.stop, 1);
retro_eventcount_notify(&s_pool.wake);
for (i = 0; i < s_pool.worker_count; i++)
sthread_join(s_pool.workers[i]);
retro_eventcount_free(&s_pool.idle);
retro_eventcount_free(&s_pool.wake);
}
free(s_pool.workers);
s_pool.workers = NULL;
s_pool.worker_count = 0;
s_pool.started = 0;
retro_atomic_store_release_int(&s_pool.owner, 0);
}
+37
View File
@@ -0,0 +1,37 @@
/* Copyright 2026 Dolphin Emulator Project
* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef DOLPHIN_COMMON_PARALLEL_FOR_H
#define DOLPHIN_COMMON_PARALLEL_FOR_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*parallel_for_fn)(void *ctx, size_t index);
/* Runs fn(ctx, i) for every i in [0, count) and returns once all of them
* have run. The calls are spread over a pool of persistent worker threads
* (one per extra core, started on first use) and the calling thread, which
* takes jobs too. No locks: jobs are claimed with an atomic counter and the
* workers park on an eventcount between batches.
*
* Calls may come from any thread. While one batch runs, a call from another
* thread runs its jobs serially on that thread instead of waiting. A job must
* not call parallel_for itself unless it accepts that serial fallback. */
void parallel_for(size_t count, parallel_for_fn fn, void *ctx);
/* Number of threads parallel_for may run jobs on, the caller included. */
unsigned parallel_for_threads(void);
/* Joins the worker threads. Call when no batch can be running (the core's
* retro_deinit); the next parallel_for starts the pool again. */
void parallel_for_deinit(void);
#ifdef __cplusplus
}
#endif
#endif
+37
View File
@@ -7,6 +7,8 @@
#include <tuple>
#endif
#include <rthreads/rthreads.h>
// Don't include Common.h here as it will break LogManager
#include "Common/CommonTypes.h"
@@ -30,6 +32,41 @@ void YieldCPU();
void SetCurrentThreadName(const char* name);
// An sthread entry point that calls F on the object passed as the thread's userdata:
// m_thread = sthread_create(Common::ThreadEntry<&Foo::Run>, this);
template <auto F>
struct ThreadEntryOf;
template <typename C, void (C::*F)()>
struct ThreadEntryOf<F>
{
static void Run(void* data) { (static_cast<C*>(data)->*F)(); }
};
template <auto F>
inline void ThreadEntry(void* data)
{
ThreadEntryOf<F>::Run(data);
}
// Starts a thread nobody joins. Returns false if it couldn't be started.
inline bool StartDetachedThread(void (*entry)(void*), void* data)
{
sthread_t* const thread = sthread_create(entry, data);
if (!thread)
return false;
sthread_detach(thread);
return true;
}
// Joins and clears an sthread handle; does nothing for a null one.
inline void JoinThread(sthread_t*& thread)
{
if (thread)
{
sthread_join(thread);
thread = nullptr;
}
}
#ifndef _WIN32
// Returns the lowest address of the stack and the size of the stack
std::tuple<void*, size_t> GetCurrentThreadStack();
+3 -2
View File
@@ -11,11 +11,12 @@
#include <algorithm>
#include <cstring>
#include <string>
#include <thread>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <features/features_cpu.h>
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
#ifdef _WIN32
@@ -116,7 +117,7 @@ void CPUInfo::Detect()
// This should be much more reliable and easier than trying to get the number of cores out of the
// CPUID data ourselves.
num_cores = std::max(static_cast<int>(std::thread::hardware_concurrency()), 1);
num_cores = std::max(static_cast<int>(cpu_features_get_core_amount()), 1);
// Assume CPU supports the CPUID instruction. Those that don't can barely
// boot modern OS anyway.
+8 -2
View File
@@ -19,6 +19,7 @@
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Common/Version.h"
#include "Common/WorkQueueThread.h"
#include "Core/AchievementApprovedHash.h"
@@ -374,7 +375,12 @@ bool AchievementManager::CanPause()
void AchievementManager::DoIdle()
{
std::thread([this] {
Common::StartDetachedThread(Common::ThreadEntry<&AchievementManager::IdleThread>, this);
}
void AchievementManager::IdleThread()
{
{
while (true)
{
Common::SleepCurrentThread(1000);
@@ -401,7 +407,7 @@ void AchievementManager::DoIdle()
rc_client_idle(m_client);
});
}
}).detach();
}
}
std::recursive_mutex& AchievementManager::GetLock()
+1
View File
@@ -130,6 +130,7 @@ public:
bool CanPause();
void DoIdle();
void IdleThread();
std::recursive_mutex& GetLock();
bool IsHardcoreModeActive() const;
+44 -117
View File
@@ -6,7 +6,6 @@
#include <algorithm>
#include <atomic>
#include <functional>
#include <future>
#include <mutex>
#include <optional>
#include <queue>
@@ -92,25 +91,14 @@ namespace Core
{
static bool s_wants_determinism;
#ifdef __LIBRETRO__
static std::vector<Common::ScopeGuard<Common::MoveOnlyFunction<void()>>> s_emu_thread_scope_guards;
std::unique_ptr<BootParameters> g_boot_params;
#endif
// Declarations and definitions
#ifndef __LIBRETRO__
static
#endif
std::thread s_emu_thread;
sthread_t* s_emu_thread = nullptr;
static Common::HookableEvent<Core::State> s_state_changed_event;
static bool s_is_throttler_temp_disabled = false;
#ifndef __LIBRETRO__
static
#endif
bool s_frame_step = false;
#ifndef __LIBRETRO__
static
#endif
std::atomic<bool> s_stop_frame_step;
// Threads other than the CPU thread must hold this when taking on the role of the CPU thread.
@@ -139,9 +127,6 @@ static std::queue<HostJob> s_host_jobs_queue;
static thread_local bool tls_is_cpu_thread = false;
static thread_local bool tls_is_gpu_thread = false;
#ifndef __LIBRETRO__
static
#endif
void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
WindowSystemInfo wsi);
@@ -226,7 +211,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
{
std::lock_guard lock(s_core_mutex);
if (s_emu_thread.joinable())
if (s_emu_thread)
{
if (!IsUninitialized(system))
{
@@ -235,7 +220,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
}
// The Emu Thread was stopped, synchronize with it.
s_emu_thread.join();
Common::JoinThread(s_emu_thread);
}
// Drain any left over jobs
@@ -245,23 +230,12 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
INFO_LOG_FMT(BOOT, "CPU Thread separate = {}", system.IsDualCoreMode() ? "Yes" : "No");
// Manually reactivate the video backend in case a GameINI overrides the video backend setting.
#ifndef __LIBRETRO__
VideoBackendBase::PopulateBackendInfo(wsi);
// Issue any API calls which must occur on the main thread for the graphics backend.
WindowSystemInfo prepared_wsi(wsi);
g_video_backend->PrepareWindow(prepared_wsi);
#endif
// Start the emu thread
s_state.store(State::Starting);
#ifdef __LIBRETRO__
// Do not launch EmuThread — Libretro handles it manually
g_boot_params = std::move(boot);
#else
s_emu_thread = std::thread(EmuThread, std::ref(system), std::move(boot), prepared_wsi);
#endif
return true;
}
@@ -394,11 +368,9 @@ static void CpuThread(Core::System& system, const std::optional<std::string> sav
}
}
#ifdef __LIBRETRO__
// dont enter the blocking loop if in single core mode
if (!system.IsDualCoreMode())
return;
#endif
// Enter CPU run loop. When we leave it - we are done.
system.GetCPU().Run();
@@ -461,80 +433,54 @@ static void FifoPlayerThread(Core::System& system, const std::optional<std::stri
const WindowSystemInfo& wsi)
{
using GuardType = Common::ScopeGuard<Common::MoveOnlyFunction<void()>>;
using ReturnType = std::unique_ptr<GuardType>;
const auto init_video = [&] {
DeclareAsGPUThread();
AsyncRequests::GetInstance()->SetPassthrough(!system.IsDualCoreMode());
#ifdef __LIBRETRO__
return true;
#else
// Must happen on the proper thread for some video backends, e.g. OpenGL.
return g_video_backend->Initialize(wsi);
#endif
};
const auto deinit_video = [] {
// Clear on screen messages that haven't expired
#ifndef __LIBRETRO__
OSD::ClearMessages();
g_video_backend->Shutdown();
#endif
};
if (system.IsDualCoreMode())
{
std::promise<bool> init_from_thread;
// Spawn the GPU thread.
std::thread gpu_thread{[&] {
Common::SetCurrentThreadName("Video thread");
const bool is_init = init_video();
init_from_thread.set_value(is_init);
if (!is_init)
return;
#ifndef __LIBRETRO__
system.GetFifo().RunGpuLoop();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Video Loop Ended"));
deinit_video();
#endif
}};
if (init_from_thread.get_future().get())
{
// Return a scope guard that signals the GPU thread to stop then joins it.
return std::make_unique<GuardType>([&, gpu_thread = std::move(gpu_thread)]() mutable {
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Wait for Video Loop to exit ..."));
system.GetFifo().ExitGpuLoop();
gpu_thread.join();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GPU thread stopped."));
});
}
gpu_thread.join();
}
else // SingleCore mode
{
if (init_video())
return std::make_unique<GuardType>(deinit_video);
// The frontend runs the GPU loop on its own thread; all that is left here is to make sure
// that loop is told to stop.
AsyncRequests::GetInstance()->SetPassthrough(false);
return std::make_unique<GuardType>([&system] {
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Wait for Video Loop to exit ..."));
system.GetFifo().ExitGpuLoop();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GPU thread stopped."));
});
}
return ReturnType{};
DeclareAsGPUThread();
AsyncRequests::GetInstance()->SetPassthrough(true);
return std::make_unique<GuardType>([] {});
}
namespace
{
struct EmuThreadArgs
{
Core::System* system;
std::unique_ptr<BootParameters> boot;
WindowSystemInfo wsi;
};
void EmuThreadEntry(void* data)
{
std::unique_ptr<EmuThreadArgs> args(static_cast<EmuThreadArgs*>(data));
EmuThread(*args->system, std::move(args->boot), args->wsi);
}
} // namespace
bool StartEmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
const WindowSystemInfo& wsi)
{
EmuThreadArgs* const args = new EmuThreadArgs{&system, std::move(boot), wsi};
s_emu_thread = sthread_create(EmuThreadEntry, args);
if (s_emu_thread)
return true;
delete args;
return false;
}
// Initialize and create emulation thread
// Call browser: Init():s_emu_thread().
// See the BootManager.cpp file description for a complete call schedule.
#ifndef __LIBRETRO__
static
#endif
void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
WindowSystemInfo wsi)
{
@@ -702,9 +648,8 @@ void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
UpdateTitle(system);
#ifdef __LIBRETRO__
// these are not needed if EmuThread is launched as a std::thread
if (!s_emu_thread.joinable())
// these are not needed if EmuThread is launched on its own thread
if (!s_emu_thread)
{
s_emu_thread_scope_guards.emplace_back(Common::MoveOnlyFunction<void()>(
[g = std::move(flag_guard)]() mutable { g.Exit(); }));
@@ -730,7 +675,6 @@ void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
s_emu_thread_scope_guards.emplace_back(Common::MoveOnlyFunction<void()>(
[g = std::move(wiifs_guard)]() mutable { g.Exit(); }));
}
#endif
// Become the CPU thread.
cpu_thread_func(system, savestate_path, delete_savestate);
@@ -885,9 +829,7 @@ static void RestoreStateAndUnlock(Core::System& system, const bool unpause_on_un
}
void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function
#ifdef __LIBRETRO__
, bool wait_for_completion
#endif
)
{
if (IsCPUThread())
@@ -896,9 +838,7 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
return;
}
#ifdef __LIBRETRO__
Common::OneShotEvent cpu_thread_job_finished;
#endif
// Pause the CPU (set it to stepping mode).
const bool was_running = PauseAndLock(system);
@@ -907,11 +847,8 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
{
// If the core hasn't been started, there is no active CPU thread we can race against.
function();
#ifdef __LIBRETRO__
wait_for_completion = false;
#endif
}
#ifdef __LIBRETRO__
else if (wait_for_completion)
{
// Queue the job function followed by triggering the event.
@@ -920,7 +857,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
cpu_thread_job_finished.Set();
});
}
#endif
else
{
// Queue the job function.
@@ -930,7 +866,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
// Release the CPU thread, and let it execute the callback.
RestoreStateAndUnlock(system, was_running);
#ifdef __LIBRETRO__
// If we're waiting for completion, block until the event fires.
if (wait_for_completion)
{
@@ -938,7 +873,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
while (!cpu_thread_job_finished.WaitFor(std::chrono::milliseconds(10)))
Host_YieldToUI();
}
#endif
}
// --- Callbacks for backends / engine ---
@@ -973,14 +907,12 @@ void Callback_NewField(Core::System& system)
}
AchievementManager::GetInstance().DoFrame();
#ifdef __LIBRETRO__
if (system.IsDualCoreMode())
system.GetFifo().StopGpuLoop();
// frontend waiting on audio here holds the thread while
// the frame is presented.
if (SoundStream* sound_stream = system.GetSoundStream())
sound_stream->NewField();
#endif
}
void UpdateTitle(Core::System& system)
@@ -1009,13 +941,10 @@ void Shutdown(Core::System& system)
// shut down.
// For more info read "DirectX Graphics Infrastructure (DXGI): Best Practices"
// on MSDN.
if (s_emu_thread.joinable())
s_emu_thread.join();
#ifdef __LIBRETRO__
Common::JoinThread(s_emu_thread);
for (auto& g : s_emu_thread_scope_guards)
g.Exit();
s_emu_thread_scope_guards.clear();
#endif
// Make sure there's nothing left over in case we're about to exit.
HostDispatchJobs(system);
@@ -1146,7 +1075,6 @@ CPUThreadGuard::~CPUThreadGuard()
RestoreStateAndUnlock(m_system, m_was_unpaused);
}
#ifdef __LIBRETRO__
void SingleCorePostRunShutdown()
{
#ifdef USE_MEMORYWATCHER
@@ -1164,6 +1092,5 @@ void SingleCorePostRunShutdown()
INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown");
}
}
#endif
} // namespace Core
+5 -2
View File
@@ -13,7 +13,7 @@
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Functional.h"
@@ -198,7 +198,10 @@ void UpdateTitle(Core::System& system);
WindowSystemInfo wsi);
void SingleCorePostRunShutdown();
extern std::unique_ptr<BootParameters> g_boot_params;
extern std::thread s_emu_thread;
// Starts EmuThread on its own thread (dual core); s_emu_thread is its handle.
bool StartEmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
const WindowSystemInfo& wsi);
extern sthread_t* s_emu_thread;
extern bool s_frame_step;
extern std::atomic<bool> s_stop_frame_step;
#endif
+1
View File
@@ -5,6 +5,7 @@
#include <algorithm>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
+4 -4
View File
@@ -115,10 +115,10 @@ void CPUManager::Run()
auto& power_pc = m_system.GetPowerPC();
// Start a separate time tracker thread
std::thread timing;
sthread_t* timing = nullptr;
if (Config::Get(Config::MAIN_TIME_TRACKING))
{
timing = std::thread(&CPUManager::StartTimePlayedTimer, this);
timing = sthread_create(Common::ThreadEntry<&CPUManager::StartTimePlayedTimer>, this);
}
#ifdef __LIBRETRO__
@@ -219,10 +219,10 @@ void CPUManager::Run()
}
}
if (timing.joinable())
if (timing)
{
m_time_played_finish_sync.Set();
timing.join();
Common::JoinThread(timing);
}
state_lock.unlock();
+4 -4
View File
@@ -5,7 +5,6 @@
#include <mutex>
#include <string>
#include <thread>
#include "Common/ChunkFile.h"
#include "Common/CommonPaths.h"
@@ -51,8 +50,9 @@ void DSPLLE::DoState(PointerWrap& p)
}
// Regular thread
void DSPLLE::DSPThread(DSPLLE* dsp_lle)
void DSPLLE::DSPThread(void* data)
{
DSPLLE* const dsp_lle = static_cast<DSPLLE*>(data);
Common::SetCurrentThreadName("DSP thread");
while (dsp_lle->m_is_running.IsSet())
@@ -155,7 +155,7 @@ bool DSPLLE::Initialize(bool wii, bool dsp_thread)
if (dsp_thread)
{
m_is_running.Set(true);
m_dsp_thread = std::thread(DSPThread, this);
m_dsp_thread = sthread_create(DSPThread, this);
}
return true;
@@ -169,7 +169,7 @@ void DSPLLE::DSP_StopSoundStream()
m_is_running.Clear();
m_ppc_event.Set();
m_dsp_event.Set();
m_dsp_thread.join();
Common::JoinThread(m_dsp_thread);
}
void DSPLLE::Shutdown()
+4 -3
View File
@@ -5,7 +5,8 @@
#include <atomic>
#include <mutex>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
@@ -40,10 +41,10 @@ public:
u32 DSP_UpdateRate() override;
private:
static void DSPThread(DSPLLE* dsp_lle);
static void DSPThread(void* data);
DSPCore m_dsp_core;
std::thread m_dsp_thread;
sthread_t* m_dsp_thread = nullptr;
std::mutex m_dsp_thread_mutex;
bool m_is_dsp_on_thread = false;
Common::Flag m_is_running;
+6 -4
View File
@@ -5,6 +5,7 @@
#include <bit>
#include <optional>
#include <thread>
#include "SFML/Network/IpAddress.hpp"
#include "SFML/Network/Socket.hpp"
@@ -21,6 +22,7 @@
#include "Common/MsgHandler.h"
#include "Common/Network.h"
#include "Common/ScopeGuard.h"
#include "Common/Thread.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
namespace
@@ -116,8 +118,7 @@ void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
m_upnp_httpd.close();
// Wait for read thread to exit.
if (m_read_thread.joinable())
m_read_thread.join();
Common::JoinThread(m_read_thread);
}
bool CEXIETHERNET::BuiltInBBAInterface::IsActivated()
@@ -700,8 +701,9 @@ bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
return true;
}
void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInBBAInterface* self)
void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(void* data)
{
BuiltInBBAInterface* const self = static_cast<BuiltInBBAInterface*>(data);
std::size_t datasize = 0;
while (!self->m_read_thread_shutdown.IsSet())
{
@@ -771,7 +773,7 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
bool CEXIETHERNET::BuiltInBBAInterface::RecvInit()
{
m_read_thread = std::thread(ReadThreadHandler, this);
m_read_thread = sthread_create(ReadThreadHandler, this);
return true;
}
+4 -3
View File
@@ -6,6 +6,7 @@
#include <libipc/ipc.h>
#include "Common/Logging/Log.h"
#include "Common/Thread.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
namespace ExpansionInterface
@@ -33,9 +34,9 @@ void CEXIETHERNET::IPCBBAInterface::Deactivate()
m_read_enabled.Clear();
m_read_thread_shutdown.Set();
if (m_read_thread.joinable())
if (m_read_thread)
{
m_read_thread.join();
Common::JoinThread(m_read_thread);
}
m_channel.disconnect();
@@ -67,7 +68,7 @@ bool CEXIETHERNET::IPCBBAInterface::SendFrame(const u8* const frame, const u32 s
bool CEXIETHERNET::IPCBBAInterface::RecvInit()
{
m_read_thread = std::thread(&CEXIETHERNET::IPCBBAInterface::ReadThreadHandler, this);
m_read_thread = sthread_create(Common::ThreadEntry<&CEXIETHERNET::IPCBBAInterface::ReadThreadHandler>, this);
return true;
}
@@ -20,6 +20,7 @@
#include "Common/Logging/Log.h"
#include "Common/Network.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/HW/EXI/BBA/TAPServerConnection.h"
#include "SFML/Network/IpAddress.hpp"
@@ -142,8 +143,7 @@ void TAPServerConnection::Deactivate()
{
m_read_enabled.Clear();
m_read_shutdown.Set();
if (m_read_thread.joinable())
m_read_thread.join();
Common::JoinThread(m_read_thread);
m_read_shutdown.Clear();
if (m_fd >= 0)
@@ -158,7 +158,7 @@ bool TAPServerConnection::IsActivated()
bool TAPServerConnection::RecvInit()
{
m_read_thread = std::thread(&TAPServerConnection::ReadThreadHandler, this);
m_read_thread = sthread_create(Common::ThreadEntry<&TAPServerConnection::ReadThreadHandler>, this);
return true;
}
@@ -5,7 +5,8 @@
#include <functional>
#include <string>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
@@ -37,7 +38,7 @@ private:
Common::SocketContext m_socket_context;
int m_fd = -1;
std::thread m_read_thread;
sthread_t* m_read_thread = nullptr;
Common::Flag m_read_enabled;
Common::Flag m_read_shutdown;
+5 -4
View File
@@ -8,6 +8,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/HW/EXI/EXI_Device.h"
namespace ExpansionInterface
@@ -37,8 +38,7 @@ void CEXIETHERNET::TAPNetworkInterface::Deactivate()
readEnabled.Clear();
readThreadShutdown.Set();
if (readThread.joinable())
readThread.join();
Common::JoinThread(readThread);
}
bool CEXIETHERNET::TAPNetworkInterface::IsActivated()
@@ -64,8 +64,9 @@ bool CEXIETHERNET::TAPNetworkInterface::SendFrame(const u8* frame, u32 size)
}
}
void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* self)
void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(void* data)
{
TAPNetworkInterface* const self = static_cast<TAPNetworkInterface*>(data);
while (!self->readThreadShutdown.IsSet())
{
fd_set rfds;
@@ -95,7 +96,7 @@ void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* s
bool CEXIETHERNET::TAPNetworkInterface::RecvInit()
{
readThread = std::thread(ReadThreadHandler, this);
readThread = sthread_create(ReadThreadHandler, this);
return true;
}
+5 -4
View File
@@ -11,6 +11,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#ifdef __linux__
#include <fcntl.h>
@@ -83,8 +84,7 @@ void CEXIETHERNET::TAPNetworkInterface::Deactivate()
readEnabled.Clear();
readThreadShutdown.Set();
if (readThread.joinable())
readThread.join();
Common::JoinThread(readThread);
#else
NOTIMPLEMENTED("Deactivate");
#endif
@@ -123,8 +123,9 @@ bool CEXIETHERNET::TAPNetworkInterface::SendFrame(const u8* frame, u32 size)
}
#ifdef __linux__
void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* self)
void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(void* data)
{
TAPNetworkInterface* const self = static_cast<TAPNetworkInterface*>(data);
while (!self->readThreadShutdown.IsSet())
{
fd_set rfds;
@@ -156,7 +157,7 @@ void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* s
bool CEXIETHERNET::TAPNetworkInterface::RecvInit()
{
#ifdef __linux__
readThread = std::thread(ReadThreadHandler, this);
readThread = sthread_create(ReadThreadHandler, this);
return true;
#else
NOTIMPLEMENTED("RecvInit");
+5 -4
View File
@@ -7,6 +7,7 @@
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/HW/EXI/EXI_Device.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
@@ -254,8 +255,7 @@ void CEXIETHERNET::TAPNetworkInterface::Deactivate()
CancelIoEx(mHAdapter, nullptr);
// Wait for read thread to exit.
if (readThread.joinable())
readThread.join();
Common::JoinThread(readThread);
// Clean-up handles
CloseHandle(mReadOverlapped.hEvent);
@@ -271,8 +271,9 @@ bool CEXIETHERNET::TAPNetworkInterface::IsActivated()
return mHAdapter != INVALID_HANDLE_VALUE;
}
void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* self)
void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(void* data)
{
TAPNetworkInterface* const self = static_cast<TAPNetworkInterface*>(data);
while (!self->readThreadShutdown.IsSet())
{
DWORD transferred;
@@ -359,7 +360,7 @@ bool CEXIETHERNET::TAPNetworkInterface::SendFrame(const u8* frame, u32 size)
bool CEXIETHERNET::TAPNetworkInterface::RecvInit()
{
readThread = std::thread(ReadThreadHandler, this);
readThread = sthread_create(ReadThreadHandler, this);
return true;
}
@@ -3,6 +3,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
#include "SFML/Network/IpAddress.hpp"
@@ -85,7 +86,11 @@ void CEXIETHERNET::XLinkNetworkInterface::Deactivate()
m_read_thread_shutdown.Set();
// Detach the thread and let it die on its own (leaks ~9kb of memory)
m_read_thread.detach();
if (m_read_thread)
{
sthread_detach(m_read_thread);
m_read_thread = nullptr;
}
// Close the socket
m_sf_socket.unbind();
@@ -134,9 +139,9 @@ bool CEXIETHERNET::XLinkNetworkInterface::SendFrame(const u8* frame, u32 size)
}
}
void CEXIETHERNET::XLinkNetworkInterface::ReadThreadHandler(
CEXIETHERNET::XLinkNetworkInterface* self)
void CEXIETHERNET::XLinkNetworkInterface::ReadThreadHandler(void* data)
{
XLinkNetworkInterface* const self = static_cast<XLinkNetworkInterface*>(data);
std::optional<sf::IpAddress> sender;
u16 port;
@@ -291,7 +296,7 @@ void CEXIETHERNET::XLinkNetworkInterface::ReadThreadHandler(
bool CEXIETHERNET::XLinkNetworkInterface::RecvInit()
{
m_read_thread = std::thread(ReadThreadHandler, this);
m_read_thread = sthread_create(ReadThreadHandler, this);
return true;
}
+9 -8
View File
@@ -6,7 +6,6 @@
#include <atomic>
#include <map>
#include <mutex>
#include <thread>
#include <vector>
#include "SFML/Network/IpAddress.hpp"
@@ -17,6 +16,8 @@
#include <SFML/Network.hpp>
#ifdef HAVE_CPPIPC
#include <libipc/ipc.h>
#include <rthreads/rthreads.h>
#endif
#include "Common/Flag.h"
@@ -349,10 +350,10 @@ private:
protected:
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__)
std::thread readThread;
sthread_t* readThread = nullptr;
Common::Flag readEnabled;
Common::Flag readThreadShutdown;
static void ReadThreadHandler(TAPNetworkInterface* self);
static void ReadThreadHandler(void* data);
#endif
#if defined(_WIN32)
HANDLE mHAdapter = INVALID_HANDLE_VALUE;
@@ -417,10 +418,10 @@ private:
sf::IpAddress m_sf_recipient_ip = sf::IpAddress::Any;
char m_in_frame[9004]{};
char m_out_frame[9004]{};
std::thread m_read_thread;
sthread_t* m_read_thread = nullptr;
Common::Flag m_read_enabled;
Common::Flag m_read_thread_shutdown;
static void ReadThreadHandler(XLinkNetworkInterface* self);
static void ReadThreadHandler(void* data);
#endif
};
@@ -458,10 +459,10 @@ private:
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
NetworkRef m_network_ref;
std::thread m_read_thread;
sthread_t* m_read_thread = nullptr;
Common::Flag m_read_enabled;
Common::Flag m_read_thread_shutdown;
static void ReadThreadHandler(BuiltInBBAInterface* self);
static void ReadThreadHandler(void* data);
#endif
void WriteToQueue(std::vector<u8> data);
bool WillQueueOverrun() const;
@@ -497,7 +498,7 @@ private:
bool m_active{};
ipc::channel m_channel;
std::thread m_read_thread;
sthread_t* m_read_thread = nullptr;
Common::Flag m_read_enabled;
Common::Flag m_read_thread_shutdown;
+11 -12
View File
@@ -7,7 +7,6 @@
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include <fmt/format.h>
@@ -21,35 +20,35 @@ namespace ExpansionInterface
{
u16 GeckoSockServer::server_port;
int GeckoSockServer::client_count;
std::thread GeckoSockServer::connectionThread;
sthread_t* GeckoSockServer::connectionThread = nullptr;
Common::Flag GeckoSockServer::server_running;
std::mutex GeckoSockServer::connection_lock;
std::queue<std::unique_ptr<sf::TcpSocket>> GeckoSockServer::waiting_socks;
GeckoSockServer::GeckoSockServer() : client_running(false)
{
if (!connectionThread.joinable())
connectionThread = std::thread(GeckoConnectionWaiter);
if (!connectionThread)
connectionThread = sthread_create(GeckoConnectionWaiter, nullptr);
}
GeckoSockServer::~GeckoSockServer()
{
if (clientThread.joinable())
if (clientThread)
{
--client_count;
client_running.Clear();
clientThread.join();
Common::JoinThread(clientThread);
}
if (client_count <= 0 && connectionThread.joinable())
if (client_count <= 0 && connectionThread)
{
server_running.Clear();
connectionThread.join();
Common::JoinThread(connectionThread);
}
}
void GeckoSockServer::GeckoConnectionWaiter()
void GeckoSockServer::GeckoConnectionWaiter(void*)
{
Common::SetCurrentThreadName("Gecko Connection Waiter");
@@ -93,15 +92,15 @@ bool GeckoSockServer::GetAvailableSock()
if (!waiting_socks.empty())
{
client = std::move(waiting_socks.front());
if (clientThread.joinable())
if (clientThread)
{
client_running.Clear();
clientThread.join();
Common::JoinThread(clientThread);
recv_fifo = std::deque<u8>();
send_fifo = std::deque<u8>();
}
clientThread = std::thread(&GeckoSockServer::ClientThread, this);
clientThread = sthread_create(Common::ThreadEntry<&GeckoSockServer::ClientThread>, this);
client_count++;
waiting_socks.pop();
sock_filled = true;
+5 -4
View File
@@ -8,7 +8,8 @@
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
@@ -26,7 +27,7 @@ public:
// Client for this server object
std::unique_ptr<sf::TcpSocket> client;
void ClientThread();
std::thread clientThread;
sthread_t* clientThread = nullptr;
std::mutex transfer_lock;
std::deque<u8> send_fifo;
@@ -37,11 +38,11 @@ private:
Common::Flag client_running;
// Only ever one server thread
static void GeckoConnectionWaiter();
static void GeckoConnectionWaiter(void*);
static u16 server_port;
static Common::Flag server_running;
static std::thread connectionThread;
static sthread_t* connectionThread;
static std::mutex connection_lock;
static std::queue<std::unique_ptr<sf::TcpSocket>> waiting_socks;
};
@@ -204,7 +204,7 @@ GCMemcardDirectory::GCMemcardDirectory(std::string directory, ExpansionInterface
return;
#endif
m_flush_thread = std::thread(&GCMemcardDirectory::FlushThread, this);
m_flush_thread = sthread_create(Common::ThreadEntry<&GCMemcardDirectory::FlushThread>, this);
}
void GCMemcardDirectory::FlushThread()
@@ -241,9 +241,9 @@ GCMemcardDirectory::~GCMemcardDirectory()
m_flush_trigger.Set();
#ifdef __LIBRETRO__
if (m_flush_thread.joinable())
if (m_flush_thread)
#endif
m_flush_thread.join();
Common::JoinThread(m_flush_thread);
FlushToFile();
}
@@ -5,9 +5,10 @@
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <rthreads/rthreads.h>
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Core/HW/GCMemcard/GCIFile.h"
@@ -63,5 +64,5 @@ private:
Common::Event m_flush_trigger;
std::mutex m_write_mutex;
Common::Flag m_exiting;
std::thread m_flush_thread;
sthread_t* m_flush_thread = nullptr;
};
@@ -8,7 +8,6 @@
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include <fmt/format.h>
@@ -78,16 +77,16 @@ MemoryCard::MemoryCard(std::string filename, ExpansionInterface::Slot card_slot,
// Class members (including inherited ones) have now been initialized, so
// it's safe to startup the flush thread (which reads them).
m_flush_buffer = std::make_unique<u8[]>(m_memory_card_size);
m_flush_thread = std::thread(&MemoryCard::FlushThread, this);
m_flush_thread = sthread_create(Common::ThreadEntry<&MemoryCard::FlushThread>, this);
}
MemoryCard::~MemoryCard()
{
if (m_flush_thread.joinable())
if (m_flush_thread)
{
m_flush_trigger.Set();
m_flush_thread.join();
Common::JoinThread(m_flush_thread);
}
}
+3 -2
View File
@@ -6,7 +6,8 @@
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
@@ -39,7 +40,7 @@ private:
std::string m_filename;
std::unique_ptr<u8[]> m_memcard_data;
std::unique_ptr<u8[]> m_flush_buffer;
std::thread m_flush_thread;
sthread_t* m_flush_thread = nullptr;
std::mutex m_flush_mutex;
Common::Event m_flush_trigger;
Common::Flag m_dirty;
+5 -7
View File
@@ -8,7 +8,6 @@
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
@@ -23,7 +22,7 @@ namespace SerialInterface
{
namespace
{
std::thread s_connection_thread;
sthread_t* s_connection_thread = nullptr;
std::queue<std::unique_ptr<sf::TcpSocket>> s_waiting_socks;
std::queue<std::unique_ptr<sf::TcpSocket>> s_waiting_clocks;
std::mutex s_cs_gba;
@@ -36,7 +35,7 @@ constexpr auto SEND_MAX_SIZE = 5, RECV_MAX_SIZE = 5;
// --- GameBoy Advance "Link Cable" ---
static void GBAConnectionWaiter()
static void GBAConnectionWaiter(void*)
{
s_server_running.Set();
@@ -81,8 +80,7 @@ static void GBAConnectionWaiter()
void GBAConnectionWaiter_Shutdown()
{
s_server_running.Clear();
if (s_connection_thread.joinable())
s_connection_thread.join();
Common::JoinThread(s_connection_thread);
}
template <typename T>
@@ -109,8 +107,8 @@ static std::unique_ptr<sf::TcpSocket> GetNextClock()
GBASockServer::GBASockServer()
{
if (!s_connection_thread.joinable())
s_connection_thread = std::thread(GBAConnectionWaiter);
if (!s_connection_thread)
s_connection_thread = sthread_create(GBAConnectionWaiter, nullptr);
s_num_connected = 0;
}
+12 -10
View File
@@ -5,6 +5,7 @@
#include <algorithm>
#include <mutex>
#include <thread>
#include <unordered_set>
#include <SFML/Network/UdpSocket.hpp>
@@ -590,7 +591,7 @@ void WiimoteScanner::StartThread()
if (m_scan_thread_running.IsSet())
return;
m_scan_thread_running.Set();
m_scan_thread = std::thread(&WiimoteScanner::ThreadFunc, this);
m_scan_thread = sthread_create(Common::ThreadEntry<&WiimoteScanner::ThreadFunc>, this);
}
void WiimoteScanner::StopThread()
@@ -605,7 +606,7 @@ void WiimoteScanner::StopThread()
}
m_scan_thread_running.Clear();
m_scan_thread.join();
Common::JoinThread(m_scan_thread);
}
}
@@ -681,7 +682,8 @@ void WiimoteScanner::ThreadFunc()
#if defined(__LIBRETRO__) && defined(SKIP_WIMOTESCANNER_THREAD)
return;
#endif
std::thread pool_thread(&WiimoteScanner::PoolThreadFunc, this);
sthread_t* pool_thread =
sthread_create(Common::ThreadEntry<&WiimoteScanner::PoolThreadFunc>, this);
Common::SetCurrentThreadName("Wiimote Scanning Thread");
@@ -776,7 +778,7 @@ void WiimoteScanner::ThreadFunc()
m_backends.clear();
}
pool_thread.join();
Common::JoinThread(pool_thread);
NOTICE_LOG_FMT(WIIMOTE, "Wiimote scanning thread has stopped.");
}
@@ -790,26 +792,26 @@ bool Wiimote::Connect(int index)
void Wiimote::StartThread()
{
if (m_write_thread.joinable())
if (m_write_thread)
return;
m_run_thread.Set();
// Note that the write thread starts the read thread.
m_write_thread = std::thread(&Wiimote::WriteThreadFunc, this);
m_write_thread = sthread_create(Common::ThreadEntry<&Wiimote::WriteThreadFunc>, this);
m_thread_ready_event.Wait();
}
void Wiimote::StopThread()
{
if (!m_write_thread.joinable())
if (!m_write_thread)
return;
m_run_thread.Clear();
retro_eventcount_notify(&m_write_event);
// Note that the write thread stops the read thread.
m_write_thread.join();
Common::JoinThread(m_write_thread);
}
void Wiimote::ReadThreadFunc()
@@ -848,7 +850,7 @@ void Wiimote::WriteThreadFunc()
return;
}
std::thread read_thread{&Wiimote::ReadThreadFunc, this};
sthread_t* read_thread = sthread_create(Common::ThreadEntry<&Wiimote::ReadThreadFunc>, this);
// Windows and also the DolphinBar require performing a write
// to detect disconnections in a timely manner
@@ -901,7 +903,7 @@ void Wiimote::WriteThreadFunc()
}
IOWakeup();
read_thread.join();
Common::JoinThread(read_thread);
NOTICE_LOG_FMT(WIIMOTE, "Disconnecting Wiimote {}.", m_index + 1);
DisconnectInternal();
@@ -8,7 +8,6 @@
#include <mutex>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
#include "Common/Config/Config.h"
@@ -25,6 +24,8 @@
#include <retro_spsc.h>
#include <rthreads/retro_eventcount.h>
#include <rthreads/rthreads.h>
class PointerWrap;
namespace WiimoteReal
@@ -172,7 +173,7 @@ private:
// And we track the rumble state to drop unnecessary rumble reports.
bool m_rumble_state = false;
std::thread m_write_thread;
sthread_t* m_write_thread = nullptr;
// Whether to keep running the thread.
Common::Flag m_run_thread;
// Triggered when the thread has finished ConnectInternal.
@@ -237,7 +238,7 @@ private:
std::vector<std::unique_ptr<WiimoteScannerBackend>> m_backends;
mutable std::mutex m_backends_mutex;
std::thread m_scan_thread;
sthread_t* m_scan_thread = nullptr;
Common::Flag m_scan_thread_running;
Common::Flag m_populate_devices;
Common::Event m_scan_mode_changed_or_population_event;
@@ -174,7 +174,8 @@ NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string&
m_handle_mail = !ios.GetIOSC().IsUsingDefaultId() && !m_send_list.IsDisabled();
m_scheduler_work_queue.Reset("WiiConnect24 Scheduler Worker");
m_scheduler_timer_thread = std::thread([this] { SchedulerTimer(); });
m_scheduler_timer_thread =
sthread_create(Common::ThreadEntry<&NetKDRequestDevice::SchedulerTimer>, this);
}
NetKDRequestDevice::~NetKDRequestDevice()
@@ -185,13 +186,13 @@ NetKDRequestDevice::~NetKDRequestDevice()
{
std::lock_guard lg(m_scheduler_lock);
if (!m_scheduler_timer_thread.joinable())
if (!m_scheduler_timer_thread)
return;
m_shutdown_event.Set();
}
m_scheduler_timer_thread.join();
Common::JoinThread(m_scheduler_timer_thread);
m_scheduler_work_queue.Shutdown();
m_work_queue.Shutdown();
}
@@ -7,7 +7,8 @@
#include <memory>
#include <queue>
#include <string>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
@@ -125,6 +126,6 @@ private:
bool m_handle_mail;
Common::Event m_shutdown_event;
std::mutex m_scheduler_lock;
std::thread m_scheduler_timer_thread;
sthread_t* m_scheduler_timer_thread = nullptr;
};
} // namespace IOS::HLE
+15 -13
View File
@@ -8,7 +8,6 @@
#include <mutex>
#include <ranges>
#include <set>
#include <thread>
#include <utility>
#ifdef __LIBUSB__
@@ -99,24 +98,27 @@ void USBScanner::StartScanning()
{
if (m_thread_running.TestAndSet())
{
m_thread = std::thread([this] {
Common::SetCurrentThreadName("USB Scan Thread");
while (m_thread_running.IsSet())
{
if (UpdateDevices())
m_first_scan_complete_flag.Set(true);
Common::SleepCurrentThread(50);
}
m_devices.clear();
m_first_scan_complete_flag.Set(false);
});
m_thread = sthread_create(Common::ThreadEntry<&USBScanner::ScanThread>, this);
}
}
void USBScanner::ScanThread()
{
Common::SetCurrentThreadName("USB Scan Thread");
while (m_thread_running.IsSet())
{
if (UpdateDevices())
m_first_scan_complete_flag.Set(true);
Common::SleepCurrentThread(50);
}
m_devices.clear();
m_first_scan_complete_flag.Set(false);
}
void USBScanner::StopScanning()
{
if (m_thread_running.TestAndClear())
m_thread.join();
Common::JoinThread(m_thread);
}
USBScanner::DeviceMap USBScanner::GetDevices() const
+4 -2
View File
@@ -7,7 +7,8 @@
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
@@ -44,6 +45,7 @@ private:
void StopScanning();
bool UpdateDevices();
void ScanThread();
bool AddNewDevices(DeviceMap* new_devices) const;
static void WakeupSantrollerDevice(libusb_device* device);
static void AddEmulatedDevices(DeviceMap* new_devices);
@@ -56,7 +58,7 @@ private:
std::mutex m_clients_mutex;
Common::Flag m_thread_running;
std::thread m_thread;
sthread_t* m_thread = nullptr;
std::mutex m_thread_start_stop_mutex;
Common::WaitableFlag m_first_scan_complete_flag;
+4 -5
View File
@@ -4,7 +4,6 @@
#include "Core/LibusbUtils.h"
#include <mutex>
#include <thread>
#if defined(__LIBUSB__)
#include <libusb.h>
@@ -36,7 +35,7 @@ public:
WARN_LOG_FMT(IOS_USB, "Failed to set LIBUSB_OPTION_USE_USBDK: {}", ErrorWrap(usbdk_ret));
#endif
m_event_thread_running.Set();
m_event_thread = std::thread(&Impl::EventThread, this);
m_event_thread = sthread_create(Common::ThreadEntry<&Impl::EventThread>, this);
}
~Impl()
@@ -46,7 +45,7 @@ public:
#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000105
libusb_interrupt_event_handler(m_context);
#endif
m_event_thread.join();
Common::JoinThread(m_event_thread);
libusb_exit(m_context);
}
@@ -58,7 +57,7 @@ public:
#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000105
libusb_interrupt_event_handler(m_context);
#endif
m_event_thread.join();
Common::JoinThread(m_event_thread);
libusb_exit(m_context);
m_context = nullptr; // clear this so it no longer fires in deconstructor again
}
@@ -100,7 +99,7 @@ private:
libusb_context* m_context = nullptr;
mutable std::mutex m_device_list_mutex;
Common::Flag m_event_thread_running;
std::thread m_event_thread;
sthread_t* m_event_thread = nullptr;
};
#else
class Context::Impl
+4 -3
View File
@@ -131,8 +131,9 @@ static void CheckKR(const char* name, kern_return_t kr)
}
}
static void ExceptionThread(mach_port_t port)
static void ExceptionThread(void* data)
{
const mach_port_t port = static_cast<mach_port_t>(reinterpret_cast<uintptr_t>(data));
Common::SetCurrentThreadName("Mach exception thread");
#pragma pack(4)
struct alignas(8)
@@ -229,8 +230,8 @@ void InstallExceptionHandler()
mach_port_t port;
CheckKR("mach_port_allocate",
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port));
std::thread exc_thread(ExceptionThread, port);
exc_thread.detach();
Common::StartDetachedThread(ExceptionThread,
reinterpret_cast<void*>(static_cast<uintptr_t>(port)));
// Obtain a send right for thread_set_exception_ports to copy...
CheckKR("mach_port_insert_right",
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND));
+4 -7
View File
@@ -10,7 +10,6 @@
#include <locale>
#include <mbedtls/md.h>
#include <mutex>
#include <thread>
#include <utility>
#include <variant>
#include <vector>
@@ -28,6 +27,7 @@
#include "Common/MsgHandler.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Common/Timer.h"
#include "Common/VariantUtil.h"
#include "Common/Version.h"
@@ -199,8 +199,7 @@ void MovieManager::Init(const BootParameters& boot)
if (IsPlayingInput())
{
ReadHeader();
std::thread md5thread(&MovieManager::CheckMD5, this);
md5thread.detach();
Common::StartDetachedThread(Common::ThreadEntry<&MovieManager::CheckMD5>, this);
if (strncmp(m_temp_header.gameID.data(), SConfig::GetInstance().GetGameID().c_str(), 6))
{
PanicAlertFmtT("The recorded game ({0}) is not the same as the selected game ({1})",
@@ -212,8 +211,7 @@ void MovieManager::Init(const BootParameters& boot)
if (IsRecordingInput())
{
GetSettings();
std::thread md5thread(&MovieManager::GetMD5, this);
md5thread.detach();
Common::StartDetachedThread(Common::ThreadEntry<&MovieManager::GetMD5>, this);
m_tick_count_at_last_input = 0;
}
@@ -486,8 +484,7 @@ bool MovieManager::BeginRecordingInput(const ControllerTypeArray& controllers,
State::SaveAs(m_system, save_path);
m_recording_from_save_state = true;
std::thread md5thread(&MovieManager::GetMD5, this);
md5thread.detach();
Common::StartDetachedThread(Common::ThreadEntry<&MovieManager::GetMD5>, this);
GetSettings();
}
+12 -5
View File
@@ -5,11 +5,13 @@
#include <atomic>
#include <chrono>
#include <thread>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MemArena.h"
#include "Common/Thread.h"
#include "DiscIO/DiscScrubber.h"
#include "DiscIO/Volume.h"
@@ -20,14 +22,15 @@ class CacheFiller final
{
public:
explicit CacheFiller(std::unique_ptr<BlobReader> reader, bool attempt_to_scrub)
: m_thread{&CacheFiller::ThreadFunc, this, std::move(reader), attempt_to_scrub}
: m_thread_reader{std::move(reader)}, m_attempt_to_scrub{attempt_to_scrub}
{
m_thread = sthread_create(Common::ThreadEntry<&CacheFiller::ThreadFunc>, this);
}
~CacheFiller()
{
m_stop_thread.store(true, std::memory_order_relaxed);
m_thread.join();
Common::JoinThread(m_thread);
}
bool Read(u64 offset, u64 size, u8* out_ptr)
@@ -81,8 +84,10 @@ private:
return CacheState::Cached;
}
void ThreadFunc(std::unique_ptr<BlobReader> reader, bool attempt_to_scrub)
void ThreadFunc()
{
const std::unique_ptr<BlobReader> reader = std::move(m_thread_reader);
const bool attempt_to_scrub = m_attempt_to_scrub;
static constexpr auto PERIODIC_LOG_TIME = std::chrono::seconds{1};
const auto start_time = Clock::now();
@@ -179,7 +184,9 @@ private:
DiscScrubber m_scrubber;
std::atomic_bool m_stop_thread{};
std::thread m_thread;
std::unique_ptr<BlobReader> m_thread_reader; // handed to the thread
bool m_attempt_to_scrub;
sthread_t* m_thread = nullptr;
};
class CachedBlobReader final : public BlobReader
+76 -54
View File
@@ -4,7 +4,7 @@
#include "DiscIO/VolumeVerifier.h"
#include <algorithm>
#include <future>
#include <functional>
#include <memory>
#include <optional>
#include <string>
@@ -31,8 +31,10 @@
#include "Common/Logging/Log.h"
#include "Common/MinizipUtil.h"
#include "Common/MsgHandler.h"
#include "Common/ParallelFor.h"
#include "Common/ScopeGuard.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Common/Swap.h"
#include "Common/Version.h"
#include "Core/IOS/Device.h"
@@ -66,47 +68,59 @@ void RedumpVerifier::Start(const Volume& volume)
m_disc_number = volume.GetDiscNumber().value_or(0);
m_size = volume.GetDataSize();
const Platform platform = volume.GetVolumeType();
m_platform = volume.GetVolumeType();
m_thread = sthread_create(Common::ThreadEntry<&RedumpVerifier::MatchThread>, this);
}
m_future = std::async(std::launch::async, [this, platform]() -> std::vector<PotentialMatch> {
std::string system;
DownloadState* download_state;
switch (platform)
{
case Platform::GameCubeDisc:
system = "gc";
download_state = &m_gc_download_state;
break;
RedumpVerifier::~RedumpVerifier()
{
Common::JoinThread(m_thread);
}
case Platform::WiiDisc:
system = "wii";
download_state = &m_wii_download_state;
break;
void RedumpVerifier::MatchThread()
{
m_potential_matches = FindPotentialMatches(m_platform);
}
default:
m_result.status = Status::Error;
return {};
}
std::vector<RedumpVerifier::PotentialMatch> RedumpVerifier::FindPotentialMatches(Platform platform)
{
std::string system;
DownloadState* download_state;
switch (platform)
{
case Platform::GameCubeDisc:
system = "gc";
download_state = &m_gc_download_state;
break;
{
std::lock_guard lk(download_state->mutex);
download_state->status = DownloadDatfile(system, download_state->status);
}
case Platform::WiiDisc:
system = "wii";
download_state = &m_wii_download_state;
break;
switch (download_state->status)
{
case DownloadStatus::FailButOldCacheAvailable:
ERROR_LOG_FMT(DISCIO, "Failed to fetch data from Redump.info, using old cached data instead");
[[fallthrough]];
case DownloadStatus::Success:
return ScanDatfile(ReadDatfile(system), system);
default:
m_result.status = Status::Error;
return {};
}
case DownloadStatus::Fail:
default:
m_result = {Status::Error, Common::GetStringT("Failed to connect to Redump.info")};
return {};
}
});
{
std::lock_guard lk(download_state->mutex);
download_state->status = DownloadDatfile(system, download_state->status);
}
switch (download_state->status)
{
case DownloadStatus::FailButOldCacheAvailable:
ERROR_LOG_FMT(DISCIO, "Failed to fetch data from Redump.info, using old cached data instead");
[[fallthrough]];
case DownloadStatus::Success:
return ScanDatfile(ReadDatfile(system), system);
case DownloadStatus::Fail:
default:
m_result = {Status::Error, Common::GetStringT("Failed to connect to Redump.info")};
return {};
}
}
static std::string GetPathForSystem(const std::string& system)
@@ -329,7 +343,8 @@ RedumpVerifier::Result RedumpVerifier::Finish(const Hashes<std::vector<u8>>& has
if (hashes.crc32.empty() && hashes.md5.empty() && hashes.sha1.empty())
return m_result;
const std::vector<PotentialMatch> potential_matches = m_future.get();
Common::JoinThread(m_thread);
const std::vector<PotentialMatch> potential_matches = std::move(m_potential_matches);
for (PotentialMatch p : potential_matches)
{
if (HashesMatch(hashes.crc32, p.hashes.crc32) && HashesMatch(hashes.md5, p.hashes.md5) &&
@@ -362,6 +377,7 @@ VolumeVerifier::VolumeVerifier(const Volume& volume, bool redump_verification,
{
if (!m_calculating_any_hash)
m_redump_verification = false;
m_async_worker.Reset("VolumeVerifier");
}
VolumeVerifier::~VolumeVerifier()
@@ -1097,18 +1113,14 @@ void VolumeVerifier::SetUpHashing()
}
}
void VolumeVerifier::WaitForAsyncOperations() const
void VolumeVerifier::WaitForAsyncOperations()
{
if (m_crc32_future.valid())
m_crc32_future.wait();
if (m_md5_future.valid())
m_md5_future.wait();
if (m_sha1_future.valid())
m_sha1_future.wait();
if (m_content_future.valid())
m_content_future.wait();
if (m_group_future.valid())
m_group_future.wait();
m_async_worker.WaitForCompletion();
}
void VolumeVerifier::RunAsyncTask(void* ctx, size_t index)
{
(*static_cast<std::vector<std::function<void()>>*>(ctx))[index]();
}
bool VolumeVerifier::ReadChunkAndWaitForAsyncOperations(u64 bytes_to_read)
@@ -1218,11 +1230,16 @@ void VolumeVerifier::Process()
m_excess_bytes = excess_bytes;
const u64 byte_increment = bytes_to_read - excess_bytes;
// The tasks for this chunk run on the async worker, spread over the parallel_for pool, while
// the caller reads the next chunk. The previous chunk's tasks must be done before reuse.
WaitForAsyncOperations();
m_async_tasks.clear();
if (m_calculating_any_hash)
{
if (m_hashes_to_calculate.crc32)
{
m_crc32_future = std::async(std::launch::async, [this, byte_increment] {
m_async_tasks.emplace_back([this, byte_increment] {
m_crc32_context = Common::UpdateCRC32(m_crc32_context, m_data.data(),
static_cast<size_t>(byte_increment));
});
@@ -1230,14 +1247,14 @@ void VolumeVerifier::Process()
if (m_hashes_to_calculate.md5)
{
m_md5_future = std::async(std::launch::async, [this, byte_increment] {
m_async_tasks.emplace_back([this, byte_increment] {
mbedtls_md5_update_ret(&m_md5_context, m_data.data(), byte_increment);
});
}
if (m_hashes_to_calculate.sha1)
{
m_sha1_future = std::async(std::launch::async, [this, byte_increment] {
m_async_tasks.emplace_back([this, byte_increment] {
m_sha1_context->Update(m_data.data(), byte_increment);
});
}
@@ -1245,7 +1262,7 @@ void VolumeVerifier::Process()
if (content_read)
{
m_content_future = std::async(std::launch::async, [this, read_failed, content] {
m_async_tasks.emplace_back([this, read_failed, content] {
if (read_failed || !m_volume.CheckContentIntegrity(content, m_data, m_ticket))
{
AddProblem(Severity::High, Common::FmtFormatT("Content {0:08x} is corrupt.", content.id));
@@ -1257,8 +1274,7 @@ void VolumeVerifier::Process()
if (group_read)
{
m_group_future = std::async(std::launch::async, [this, read_failed,
group_index = m_group_index] {
m_async_tasks.emplace_back([this, read_failed, group_index = m_group_index] {
const GroupToVerify& group = m_groups[group_index];
u64 offset_in_group = 0;
for (u64 block_index = group.block_index_start; block_index < group.block_index_end;
@@ -1291,6 +1307,12 @@ void VolumeVerifier::Process()
m_group_index++;
}
if (!m_async_tasks.empty())
{
m_async_worker.Push(
[this] { parallel_for(m_async_tasks.size(), RunAsyncTask, &m_async_tasks); });
}
m_progress += byte_increment;
}
+22 -8
View File
@@ -3,7 +3,7 @@
#pragma once
#include <future>
#include <functional>
#include <map>
#include <memory>
#include <optional>
@@ -12,8 +12,11 @@
#include <mbedtls/md5.h>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Crypto/SHA1.h"
#include "Common/WorkQueueThread.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/DiscScrubber.h"
#include "DiscIO/Volume.h"
@@ -58,6 +61,11 @@ public:
std::string message;
};
RedumpVerifier() = default;
~RedumpVerifier();
RedumpVerifier(const RedumpVerifier&) = delete;
RedumpVerifier& operator=(const RedumpVerifier&) = delete;
void Start(const Volume& volume);
Result Finish(const Hashes<std::vector<u8>>& hashes);
@@ -91,7 +99,13 @@ private:
u8 m_disc_number = 0;
u64 m_size = 0;
std::future<std::vector<PotentialMatch>> m_future;
void MatchThread();
std::vector<PotentialMatch> FindPotentialMatches(Platform platform);
// Looks the disc up in the Redump datfile while the disc is hashed.
sthread_t* m_thread = nullptr;
Platform m_platform{};
std::vector<PotentialMatch> m_potential_matches;
Result m_result;
static DownloadState m_gc_download_state;
@@ -155,7 +169,8 @@ private:
void CheckMisc();
void CheckSuperPaperMario();
void SetUpHashing();
void WaitForAsyncOperations() const;
void WaitForAsyncOperations();
static void RunAsyncTask(void* ctx, size_t index);
bool ReadChunkAndWaitForAsyncOperations(u64 bytes_to_read);
void AddProblem(Severity severity, std::string text);
@@ -180,11 +195,7 @@ private:
u64 m_excess_bytes = 0;
std::vector<u8> m_data;
std::future<void> m_crc32_future;
std::future<void> m_md5_future;
std::future<void> m_sha1_future;
std::future<void> m_content_future;
std::future<void> m_group_future;
std::vector<std::function<void()>> m_async_tasks;
DiscScrubber m_scrubber;
IOS::ES::TicketReader m_ticket;
@@ -203,6 +214,9 @@ private:
u64 m_progress = 0;
u64 m_max_progress = 0;
DataSizeType m_data_size_type;
// Declared last so it stops before anything its tasks touch is destroyed.
Common::AsyncWorkThreadSP m_async_worker;
};
} // namespace DiscIO
+72 -89
View File
@@ -7,13 +7,11 @@
#include <array>
#include <cstddef>
#include <cstring>
#include <future>
#include <map>
#include <memory>
#include <optional>
#include <ranges>
#include <string>
#include <thread>
#include <utility>
#include <vector>
@@ -23,6 +21,7 @@
#include "Common/Crypto/AES.h"
#include "Common/Crypto/SHA1.h"
#include "Common/Logging/Log.h"
#include "Common/ParallelFor.h"
#include "DiscIO/Blob.h"
#include "DiscIO/DiscExtractor.h"
@@ -483,75 +482,82 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const Partition& partition)
return CheckBlockIntegrity(block_index, cluster.data(), partition);
}
namespace
{
struct HashJob
{
const std::array<u8, VolumeWii::BLOCK_DATA_SIZE>* in;
VolumeWii::HashBlock* out;
};
// H0 hashes of one block, and that block's entry in the H1 table of its subgroup.
void HashBlockJob(void* ctx, size_t i)
{
const HashJob* const job = static_cast<const HashJob*>(ctx);
VolumeWii::HashBlock* const out = job->out;
const size_t h1_base = Common::AlignDown(i, 8);
for (size_t j = 0; j < 31; ++j)
out[i].h0[j] = Common::SHA1::CalculateDigest(job->in[i].data() + j * 0x400, 0x400);
out[i].padding_0 = {};
out[h1_base].h1[i - h1_base] = Common::SHA1::CalculateDigest(out[i].h0);
}
struct EncryptJob
{
const std::array<u8, VolumeWii::BLOCK_DATA_SIZE>* in;
const VolumeWii::HashBlock* hashes;
u8* out;
const Common::AES::Context* aes;
};
void EncryptBlockJob(void* ctx, size_t i)
{
const EncryptJob* const job = static_cast<const EncryptJob*>(ctx);
u8* const out_ptr = job->out + i * VolumeWii::BLOCK_TOTAL_SIZE;
job->aes->CryptIvZero(reinterpret_cast<const u8*>(&job->hashes[i]), out_ptr,
VolumeWii::BLOCK_HEADER_SIZE);
job->aes->Crypt(out_ptr + 0x3D0, job->in[i].data(), out_ptr + VolumeWii::BLOCK_HEADER_SIZE,
VolumeWii::BLOCK_DATA_SIZE);
}
} // namespace
bool VolumeWii::HashGroup(const std::array<u8, BLOCK_DATA_SIZE> in[BLOCKS_PER_GROUP],
HashBlock out[BLOCKS_PER_GROUP],
const std::function<bool(size_t block)>& read_function)
{
std::array<std::future<void>, BLOCKS_PER_GROUP> hash_futures;
bool success = true;
for (size_t i = 0; i < BLOCKS_PER_GROUP; ++i)
// The reads go through one blob reader, so they stay on this thread; the 64 blocks of hashing
// are spread over the shared worker pool.
if (read_function)
{
if (read_function && success)
success = read_function(i);
hash_futures[i] = std::async(std::launch::async, [&in, &out, &hash_futures, success, i] {
const size_t h1_base = Common::AlignDown(i, 8);
if (success)
{
// H0 hashes
for (size_t j = 0; j < 31; ++j)
out[i].h0[j] = Common::SHA1::CalculateDigest(in[i].data() + j * 0x400, 0x400);
// H0 padding
out[i].padding_0 = {};
// H1 hash
out[h1_base].h1[i - h1_base] = Common::SHA1::CalculateDigest(out[i].h0);
}
if (i % 8 == 7)
{
for (size_t j = 0; j < 7; ++j)
hash_futures[h1_base + j].get();
if (success)
{
// H1 padding
out[h1_base].padding_1 = {};
// H1 copies
for (size_t j = 1; j < 8; ++j)
out[h1_base + j].h1 = out[h1_base].h1;
// H2 hash
out[0].h2[h1_base / 8] = Common::SHA1::CalculateDigest(out[i].h1);
}
if (i == BLOCKS_PER_GROUP - 1)
{
for (size_t j = 0; j < 7; ++j)
hash_futures[j * 8 + 7].get();
if (success)
{
// H2 padding
out[0].padding_2 = {};
// H2 copies
for (size_t j = 1; j < BLOCKS_PER_GROUP; ++j)
out[j].h2 = out[0].h2;
}
}
}
});
for (size_t i = 0; i < BLOCKS_PER_GROUP; ++i)
{
if (!read_function(i))
return false;
}
}
// Wait for all the async tasks to finish
hash_futures.back().get();
HashJob job{in, out};
parallel_for(BLOCKS_PER_GROUP, HashBlockJob, &job);
return success;
for (size_t h1_base = 0; h1_base < BLOCKS_PER_GROUP; h1_base += 8)
{
// H1 padding and copies
out[h1_base].padding_1 = {};
for (size_t j = 1; j < 8; ++j)
out[h1_base + j].h1 = out[h1_base].h1;
// H2 hash
out[0].h2[h1_base / 8] = Common::SHA1::CalculateDigest(out[h1_base].h1);
}
// H2 padding and copies
out[0].padding_2 = {};
for (size_t j = 1; j < BLOCKS_PER_GROUP; ++j)
out[j].h2 = out[0].h2;
return true;
}
bool VolumeWii::EncryptGroup(
@@ -586,34 +592,11 @@ bool VolumeWii::EncryptGroup(
if (hash_exception_callback)
hash_exception_callback(unencrypted_hashes.data());
const unsigned int threads =
std::min(BLOCKS_PER_GROUP, std::max<unsigned int>(1, std::thread::hardware_concurrency()));
std::vector<std::future<void>> encryption_futures(threads);
auto aes_context = Common::AES::CreateContextEncrypt(key.data());
for (size_t i = 0; i < threads; ++i)
{
encryption_futures[i] = std::async(
std::launch::async,
[&unencrypted_data, &unencrypted_hashes, &aes_context, &out](size_t start, size_t end) {
for (size_t j = start; j < end; ++j)
{
u8* out_ptr = out->data() + j * BLOCK_TOTAL_SIZE;
aes_context->CryptIvZero(reinterpret_cast<u8*>(&unencrypted_hashes[j]), out_ptr,
BLOCK_HEADER_SIZE);
aes_context->Crypt(out_ptr + 0x3D0, unencrypted_data[j].data(),
out_ptr + BLOCK_HEADER_SIZE, BLOCK_DATA_SIZE);
}
},
i * BLOCKS_PER_GROUP / threads, (i + 1) * BLOCKS_PER_GROUP / threads);
}
for (std::future<void>& future : encryption_futures)
future.get();
EncryptJob job{unencrypted_data.data(), unencrypted_hashes.data(), out->data(),
aes_context.get()};
parallel_for(BLOCKS_PER_GROUP, EncryptBlockJob, &job);
return true;
}
+3 -2
View File
@@ -15,6 +15,7 @@
#include "Common/FileUtil.h"
#include "Common/GL/GLContext.h"
#include "Common/Logging/LogManager.h"
#include "Common/ParallelFor.h"
#include "Common/Thread.h"
#include "Common/scmrev.h"
#include "Common/Version.h"
@@ -162,6 +163,7 @@ void retro_init(void)
void retro_deinit(void)
{
Libretro::g_emuthread_launched = false;
parallel_for_deinit();
#ifdef PERF_TEST
perf_cb.perf_log();
#endif
@@ -286,8 +288,7 @@ void retro_run(void)
WindowSystemInfo wsi(WindowSystemType::Libretro, nullptr, nullptr, nullptr);
if (system.IsDualCoreMode())
{
Core::s_emu_thread = std::thread(Core::EmuThread,
std::ref(system), std::move(Core::g_boot_params), wsi);
Core::StartEmuThread(system, std::move(Core::g_boot_params), wsi);
// Wait until CPU thread has reached Run()
auto& cpu_manager = system.GetCPU();
@@ -6,7 +6,6 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <thread>
#include <tuple>
#include <SFML/Network/SocketSelector.hpp>
@@ -215,7 +214,7 @@ private:
std::vector<Server> m_servers;
u32 m_client_uid = 0;
SteadyClock::time_point m_next_listports_time;
std::thread m_hotplug_thread;
sthread_t* m_hotplug_thread = nullptr;
Common::Flag m_hotplug_thread_running;
Config::ConfigChangedCallbackID m_config_change_callback_id;
};
@@ -372,7 +371,7 @@ void InputBackend::StartHotplugThread()
return;
}
m_hotplug_thread = std::thread(&InputBackend::HotplugThreadFunc, this);
m_hotplug_thread = sthread_create(Common::ThreadEntry<&InputBackend::HotplugThreadFunc>, this);
}
void InputBackend::StopHotplugThread()
@@ -384,7 +383,7 @@ void InputBackend::StopHotplugThread()
return;
}
m_hotplug_thread.join();
Common::JoinThread(m_hotplug_thread);
for (auto& server : m_servers)
{
+16 -16
View File
@@ -78,8 +78,8 @@ static void ResetRumbleLockNeeded();
static void Reset();
static void Setup();
static void ProcessInputPayload(const u8* data, std::size_t size);
static void ReadThreadFunc();
static void WriteThreadFunc();
static void ReadThreadFunc(void*);
static void WriteThreadFunc(void*);
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
enum class AdapterStatus
@@ -130,9 +130,9 @@ static std::array<PortState, SerialInterface::MAX_SI_CHANNELS> s_port_states;
static std::array<u8, CONTROLLER_OUTPUT_RUMBLE_PAYLOAD_SIZE> s_controller_write_payload;
static std::atomic<int> s_controller_write_payload_size{0};
static std::thread s_read_adapter_thread;
static sthread_t* s_read_adapter_thread = nullptr;
static Common::Flag s_read_adapter_thread_running;
static std::thread s_write_adapter_thread;
static sthread_t* s_write_adapter_thread = nullptr;
static Common::Flag s_write_adapter_thread_running;
static Common::Event s_write_happened;
@@ -142,7 +142,7 @@ static std::mutex s_read_mutex;
static std::mutex s_write_mutex;
#endif
static std::thread s_adapter_detect_thread;
static sthread_t* s_adapter_detect_thread = nullptr;
static Common::Flag s_adapter_detect_thread_running;
static Common::Event s_hotplug_event;
@@ -178,7 +178,7 @@ static std::array<std::atomic_bool, SerialInterface::MAX_SI_CHANNELS> s_config_r
static std::atomic<double> s_adapter_poll_rate{};
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION || GCADAPTER_USE_ANDROID_IMPLEMENTATION
static void ReadThreadFunc()
static void ReadThreadFunc(void*)
{
Common::SetCurrentThreadName("GCAdapter Read Thread");
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "GCAdapter read thread started");
@@ -209,7 +209,7 @@ static void ReadThreadFunc()
#endif
s_write_adapter_thread_running.Set(true);
s_write_adapter_thread = std::thread(WriteThreadFunc);
s_write_adapter_thread = sthread_create(WriteThreadFunc, nullptr);
std::chrono::steady_clock::time_point last_successful_read_time =
std::chrono::steady_clock::now();
@@ -300,7 +300,7 @@ static void ReadThreadFunc()
s_controller_write_payload_size.store(0);
// Kick the waiting event
s_write_happened.Set();
s_write_adapter_thread.join();
Common::JoinThread(s_write_adapter_thread);
}
#if GCADAPTER_USE_ANDROID_IMPLEMENTATION
@@ -315,7 +315,7 @@ static void ReadThreadFunc()
#endif
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION || GCADAPTER_USE_ANDROID_IMPLEMENTATION
static void WriteThreadFunc()
static void WriteThreadFunc(void*)
{
Common::SetCurrentThreadName("GCAdapter Write Thread");
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "GCAdapter write thread started");
@@ -411,7 +411,7 @@ Java_org_dolphinemu_dolphinemu_utils_GCAdapter_onAdapterDisconnected(JNIEnv* env
}
#endif
static void ScanThreadFunc()
static void ScanThreadFunc(void*)
{
Common::SetCurrentThreadName("GC Adapter Scanning Thread");
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "GC Adapter scanning thread started");
@@ -513,7 +513,7 @@ static void StartScanThread()
return;
#endif
s_adapter_detect_thread_running.Set(true);
s_adapter_detect_thread = std::thread(ScanThreadFunc);
s_adapter_detect_thread = sthread_create(ScanThreadFunc, nullptr);
}
static void StopScanThread()
@@ -521,7 +521,7 @@ static void StopScanThread()
if (s_adapter_detect_thread_running.TestAndClear())
{
s_hotplug_event.Set();
s_adapter_detect_thread.join();
Common::JoinThread(s_adapter_detect_thread);
}
}
@@ -617,10 +617,10 @@ static void Setup()
// Make sure the thread isn't in the middle of shutting down while starting a new one
if (s_read_adapter_thread_running.TestAndClear())
s_read_adapter_thread.join();
Common::JoinThread(s_read_adapter_thread);
s_read_adapter_thread_running.Set(true);
s_read_adapter_thread = std::thread(ReadThreadFunc);
s_read_adapter_thread = sthread_create(ReadThreadFunc, nullptr);
#endif
}
#endif
@@ -772,7 +772,7 @@ static void AddGCAdapter(libusb_device* device)
}
s_read_adapter_thread_running.Set(true);
s_read_adapter_thread = std::thread(ReadThreadFunc);
s_read_adapter_thread = sthread_create(ReadThreadFunc, nullptr);
s_status = AdapterStatus::Detected;
if (s_detect_callback != nullptr)
@@ -812,7 +812,7 @@ static void Reset()
#endif
if (s_read_adapter_thread_running.TestAndClear())
s_read_adapter_thread.join();
Common::JoinThread(s_read_adapter_thread);
// The read thread will close the write thread
s_read_thread_has_init.Clear();
@@ -24,9 +24,17 @@ void CustomAssetLoader::Shutdown()
bool CustomAssetLoader::StartWorkerThreads(u32 num_worker_threads)
{
// Each thread is handed its slot, so the vector must not move once threads are running.
m_worker_threads.reserve(num_worker_threads);
for (u32 i = 0; i < num_worker_threads; i++)
{
m_worker_threads.emplace_back(&CustomAssetLoader::WorkerThreadRun, this, i);
WorkerThread& worker = m_worker_threads.emplace_back(WorkerThread{this, i, nullptr});
worker.thread = sthread_create(WorkerThreadEntry, &worker);
if (!worker.thread)
{
m_worker_threads.pop_back();
break;
}
}
return HasWorkerThreads();
@@ -59,12 +67,18 @@ void CustomAssetLoader::StopWorkerThreads()
}
// Wait for worker threads to exit.
for (std::thread& thr : m_worker_threads)
thr.join();
for (WorkerThread& worker : m_worker_threads)
Common::JoinThread(worker.thread);
m_worker_threads.clear();
m_exit_flag.Clear();
}
void CustomAssetLoader::WorkerThreadEntry(void* data)
{
const WorkerThread* const worker = static_cast<const WorkerThread*>(data);
worker->owner->WorkerThreadRun(worker->index);
}
void CustomAssetLoader::WorkerThreadRun(u32 thread_index)
{
#if defined(__LIBRETRO__) && defined(SKIP_ASSETLOADER_THREAD)
@@ -8,9 +8,10 @@
#include <list>
#include <mutex>
#include <set>
#include <thread>
#include <vector>
#include <rthreads/rthreads.h>
#include "Common/Flag.h"
#include "VideoCommon/Assets/CustomAsset.h"
@@ -56,11 +57,18 @@ private:
bool HasWorkerThreads() const;
void StopWorkerThreads();
struct WorkerThread
{
CustomAssetLoader* owner;
u32 index;
sthread_t* thread;
};
static void WorkerThreadEntry(void* data);
void WorkerThreadRun(u32 thread_index);
Common::Flag m_exit_flag;
std::vector<std::thread> m_worker_threads;
std::vector<WorkerThread> m_worker_threads;
std::mutex m_assets_to_load_lock;
std::list<CustomAsset*> m_assets_to_load;
@@ -140,17 +140,26 @@ bool AsyncShaderCompiler::StartWorkerThreads(u32 num_worker_threads)
m_worker_thread_start_result.store(false);
std::thread thr(&AsyncShaderCompiler::WorkerThreadEntryPoint, this, thread_param);
// The new thread takes its parameter before it signals m_init_event.
m_starting_thread_param = thread_param;
sthread_t* thr = sthread_create(Common::ThreadEntry<&AsyncShaderCompiler::WorkerThreadStart>,
this);
if (!thr)
{
WorkerThreadExit(thread_param);
WARN_LOG_FMT(VIDEO, "Failed to start shader compiler worker thread.");
break;
}
m_init_event.Wait();
if (!m_worker_thread_start_result.load())
{
WARN_LOG_FMT(VIDEO, "Failed to start shader compiler worker thread.");
thr.join();
Common::JoinThread(thr);
break;
}
m_worker_threads.push_back(std::move(thr));
m_worker_threads.push_back(thr);
}
return HasWorkerThreads();
@@ -183,8 +192,8 @@ void AsyncShaderCompiler::StopWorkerThreads()
}
// Wait for worker threads to exit.
for (std::thread& thr : m_worker_threads)
thr.join();
for (sthread_t*& thr : m_worker_threads)
Common::JoinThread(thr);
m_worker_threads.clear();
m_exit_flag.Clear();
}
@@ -203,6 +212,11 @@ void AsyncShaderCompiler::WorkerThreadExit(void* param)
{
}
void AsyncShaderCompiler::WorkerThreadStart()
{
WorkerThreadEntryPoint(m_starting_thread_param);
}
void AsyncShaderCompiler::WorkerThreadEntryPoint(void* param)
{
Common::SetCurrentThreadName("AsyncShaderCompiler Worker");
@@ -10,10 +10,11 @@
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
#include <rthreads/rthreads.h>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Flag.h"
@@ -68,13 +69,15 @@ protected:
virtual void WorkerThreadExit(void* param);
private:
void WorkerThreadStart();
void WorkerThreadEntryPoint(void* param);
void WorkerThreadRun();
Common::Flag m_exit_flag;
Common::Event m_init_event;
std::vector<std::thread> m_worker_threads;
std::vector<sthread_t*> m_worker_threads;
void* m_starting_thread_param = nullptr;
std::atomic_bool m_worker_thread_start_result{false};
// A multimap is used to store the work items. We can't use a priority_queue here, because
+4 -5
View File
@@ -6,6 +6,7 @@
#include "Common/Assert.h"
#include "Common/FileUtil.h"
#include "Common/Image.h"
#include "Common/Thread.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
@@ -159,8 +160,7 @@ void FrameDumper::ShutdownFrameDumping()
// Wake thread up, and wait for it to exit.
m_frame_dump_thread_running.Clear();
m_frame_dump_start.Set();
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
Common::JoinThread(m_frame_dump_thread);
m_frame_dump_render_framebuffer.reset();
m_frame_dump_render_texture.reset();
@@ -174,10 +174,9 @@ void FrameDumper::DumpFrameData(const u8* data, int w, int h, int stride)
if (!m_frame_dump_thread_running.IsSet())
{
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
Common::JoinThread(m_frame_dump_thread);
m_frame_dump_thread_running.Set();
m_frame_dump_thread = std::thread(&FrameDumper::FrameDumpThreadFunc, this);
m_frame_dump_thread = sthread_create(Common::ThreadEntry<&FrameDumper::FrameDumpThreadFunc>, this);
}
// Wake worker thread up.
+1 -2
View File
@@ -3,7 +3,6 @@
#pragma once
#include <thread>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
@@ -63,7 +62,7 @@ private:
// Ensures all encoded frames have been written to the output file.
void FinishFrameData();
std::thread m_frame_dump_thread;
sthread_t* m_frame_dump_thread = nullptr;
Common::Flag m_frame_dump_thread_running;
// Used to kick frame dump thread.