mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
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:
1 parent
8c7aa58f6b
commit
767c898df8
52 files changed
+771
-451
No files matched your search
@@ -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 \
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -130,6 +130,7 @@ public:
|
||||
|
||||
bool CanPause();
|
||||
void DoIdle();
|
||||
void IdleThread();
|
||||
|
||||
std::recursive_mutex& GetLock();
|
||||
bool IsHardcoreModeActive() const;
|
||||
|
||||
+44
-117
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
+3
-4
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user