2024-07-30 11:42:36 +00:00
|
|
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
|
|
|
// SPDX-License-Identifier: GPL-3.0+
|
2009-10-21 08:57:05 +00:00
|
|
|
|
2009-09-08 12:08:10 +00:00
|
|
|
#pragma once
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2021-09-01 20:31:46 +00:00
|
|
|
#include "common/Pcsx2Defs.h"
|
2022-05-09 10:28:57 +00:00
|
|
|
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
#include <mach/semaphore.h>
|
|
|
|
#elif !defined(_WIN32)
|
|
|
|
#include <semaphore.h>
|
|
|
|
#endif
|
|
|
|
|
2022-03-25 10:12:52 +00:00
|
|
|
#include <atomic>
|
2022-05-07 11:07:22 +00:00
|
|
|
#include <functional>
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-11-22 09:47:52 +00:00
|
|
|
namespace Threading
|
|
|
|
{
|
2022-05-09 10:28:57 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// Platform Specific External APIs
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// The following set of documented functions have Linux/Win32 specific implementations,
|
|
|
|
// which are found in WinThreads.cpp and LnxThreads.cpp
|
2022-05-05 13:01:14 +00:00
|
|
|
|
2021-09-06 18:28:26 +00:00
|
|
|
extern u64 GetThreadCpuTime();
|
|
|
|
extern u64 GetThreadTicksPerSecond();
|
2016-11-12 15:28:37 +00:00
|
|
|
|
2022-05-05 13:01:02 +00:00
|
|
|
/// Set the name of the current thread
|
|
|
|
extern void SetNameOfCurrentThread(const char* name);
|
|
|
|
|
2021-09-06 18:28:26 +00:00
|
|
|
// Releases a timeslice to other threads.
|
|
|
|
extern void Timeslice();
|
User Interface:
* Fixed and added better Emulation/System menu updating. Suspend/Resume is more consistent, and Reset grays itself out after being used.
* Entering plugin configurations auto-suspends the emulator.
* Changing plugins in the Configuration PAnel takes effect now without a restart.
* Added preliminary support for an ExtensibleConfirmation Dialog box (contains a sizer you can add content to, and also has an optional "[x] Do not show this again" checkbox).
Bugfixes:
* Added some mutex protection to cdvdNewDiskCB; "just in case."
* Resolved several recursion and deadlock scenarios when (very!) rapidly suspending, resuming, and resetting the emu.
Developments / Code Cleanups:
* Renamed SysCoreThread ExecutionModes: Suspend/Resume are now Opened/Closed (which more accurately reflects the fact they opena nd close the plugins, and helps avoid ambiguity with the "Paused" state).
* Added Exception::ThreadTimedOut, which is now thrown from Semaphore::Wait when recursive wxApp::Yield() calls are detected, and a deadlock occurs (basically cancels the current action which, most of the time, allows for full recovery).
* Major Threading namespace cleanups, documentations, etc.
* Removed wxScopedArray (scopedarray.h) and replaced it with a better implemeneted ScopedArray class.
* Removed toUTF8 class, which I only added a couple weeks ago because I didn't realize wxCharBuffer had an implicit typecast to (char*).
* Implemented more Source/Listener events for Pcsx2App. CoreThread events are sourced properly now, and added SettingsApplied and SettingsLoadSave Sources.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2010 96395faa-99c1-11dd-bbfe-3dabce05a288
2009-10-16 03:58:29 +00:00
|
|
|
|
2021-09-06 18:28:26 +00:00
|
|
|
// For use in spin/wait loops.
|
|
|
|
extern void SpinWait();
|
2016-02-22 21:23:09 +00:00
|
|
|
|
2021-09-06 18:28:26 +00:00
|
|
|
// Optional implementation to enable hires thread/process scheduler for the operating system.
|
|
|
|
// Needed by Windows, but might not be relevant to other platforms.
|
|
|
|
extern void EnableHiresScheduler();
|
|
|
|
extern void DisableHiresScheduler();
|
2009-10-19 23:13:22 +00:00
|
|
|
|
2021-09-06 18:28:26 +00:00
|
|
|
// sleeps the current thread for the given number of milliseconds.
|
|
|
|
extern void Sleep(int ms);
|
User Interface:
* Fixed and added better Emulation/System menu updating. Suspend/Resume is more consistent, and Reset grays itself out after being used.
* Entering plugin configurations auto-suspends the emulator.
* Changing plugins in the Configuration PAnel takes effect now without a restart.
* Added preliminary support for an ExtensibleConfirmation Dialog box (contains a sizer you can add content to, and also has an optional "[x] Do not show this again" checkbox).
Bugfixes:
* Added some mutex protection to cdvdNewDiskCB; "just in case."
* Resolved several recursion and deadlock scenarios when (very!) rapidly suspending, resuming, and resetting the emu.
Developments / Code Cleanups:
* Renamed SysCoreThread ExecutionModes: Suspend/Resume are now Opened/Closed (which more accurately reflects the fact they opena nd close the plugins, and helps avoid ambiguity with the "Paused" state).
* Added Exception::ThreadTimedOut, which is now thrown from Semaphore::Wait when recursive wxApp::Yield() calls are detected, and a deadlock occurs (basically cancels the current action which, most of the time, allows for full recovery).
* Major Threading namespace cleanups, documentations, etc.
* Removed wxScopedArray (scopedarray.h) and replaced it with a better implemeneted ScopedArray class.
* Removed toUTF8 class, which I only added a couple weeks ago because I didn't realize wxCharBuffer had an implicit typecast to (char*).
* Implemented more Source/Listener events for Pcsx2App. CoreThread events are sourced properly now, and added SettingsApplied and SettingsLoadSave Sources.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2010 96395faa-99c1-11dd-bbfe-3dabce05a288
2009-10-16 03:58:29 +00:00
|
|
|
|
2022-12-04 04:52:26 +00:00
|
|
|
// sleeps the current thread until the specified time point, or later.
|
|
|
|
extern void SleepUntil(u64 ticks);
|
|
|
|
|
2022-05-02 06:01:44 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// ThreadHandle
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// Abstracts an OS's handle to a thread, closing the handle when necessary. Currently,
|
|
|
|
// only used for getting the CPU time for a thread.
|
|
|
|
//
|
|
|
|
class ThreadHandle
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ThreadHandle();
|
|
|
|
ThreadHandle(ThreadHandle&& handle);
|
|
|
|
ThreadHandle(const ThreadHandle& handle);
|
|
|
|
~ThreadHandle();
|
|
|
|
|
|
|
|
/// Returns a new handle for the calling thread.
|
|
|
|
static ThreadHandle GetForCallingThread();
|
|
|
|
|
|
|
|
ThreadHandle& operator=(ThreadHandle&& handle);
|
|
|
|
ThreadHandle& operator=(const ThreadHandle& handle);
|
|
|
|
|
|
|
|
operator void*() const { return m_native_handle; }
|
|
|
|
operator bool() const { return (m_native_handle != nullptr); }
|
|
|
|
|
|
|
|
/// Returns the amount of CPU time consumed by the thread, at the GetThreadTicksPerSecond() frequency.
|
|
|
|
u64 GetCPUTime() const;
|
|
|
|
|
|
|
|
/// Sets the affinity for a thread to the specified processors.
|
|
|
|
/// Obviously, only works up to 64 processors.
|
|
|
|
bool SetAffinity(u64 processor_mask) const;
|
|
|
|
|
2022-05-07 11:07:22 +00:00
|
|
|
protected:
|
2022-05-02 06:01:44 +00:00
|
|
|
void* m_native_handle = nullptr;
|
|
|
|
|
|
|
|
// We need the thread ID for affinity adjustments on Linux.
|
|
|
|
#if defined(__linux__)
|
|
|
|
unsigned int m_native_id = 0;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2022-05-07 11:07:22 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// Thread
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// Abstracts a native thread in a lightweight manner. Provides more functionality than
|
|
|
|
// std::thread (allowing stack size adjustments).
|
|
|
|
//
|
|
|
|
class Thread : public ThreadHandle
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using EntryPoint = std::function<void()>;
|
|
|
|
|
|
|
|
Thread();
|
|
|
|
Thread(Thread&& thread);
|
|
|
|
Thread(const Thread&) = delete;
|
|
|
|
Thread(EntryPoint func);
|
|
|
|
~Thread();
|
|
|
|
|
|
|
|
ThreadHandle& operator=(Thread&& thread);
|
|
|
|
ThreadHandle& operator=(const Thread& handle) = delete;
|
|
|
|
|
|
|
|
__fi bool Joinable() const { return (m_native_handle != nullptr); }
|
|
|
|
__fi u32 GetStackSize() const { return m_stack_size; }
|
|
|
|
|
|
|
|
/// Sets the stack size for the thread. Do not call if the thread has already been started.
|
|
|
|
void SetStackSize(u32 size);
|
|
|
|
|
|
|
|
bool Start(EntryPoint func);
|
|
|
|
void Detach();
|
|
|
|
void Join();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
#ifdef _WIN32
|
|
|
|
static unsigned __stdcall ThreadProc(void* param);
|
|
|
|
#else
|
|
|
|
static void* ThreadProc(void* param);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
u32 m_stack_size = 0;
|
|
|
|
};
|
|
|
|
|
2022-03-25 10:12:52 +00:00
|
|
|
/// A semaphore that may not have a fast userspace path
|
|
|
|
/// (Used in other semaphore-based algorithms where the semaphore is just used for its thread sleep/wake ability)
|
|
|
|
class KernelSemaphore
|
|
|
|
{
|
|
|
|
#if defined(_WIN32)
|
|
|
|
void* m_sema;
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
semaphore_t m_sema;
|
|
|
|
#else
|
|
|
|
sem_t m_sema;
|
|
|
|
#endif
|
|
|
|
public:
|
|
|
|
KernelSemaphore();
|
|
|
|
~KernelSemaphore();
|
|
|
|
void Post();
|
|
|
|
void Wait();
|
2022-05-05 13:01:12 +00:00
|
|
|
bool TryWait();
|
2022-03-25 10:12:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/// A semaphore for notifying a work-processing thread of new work in a (separate) queue
|
|
|
|
///
|
|
|
|
/// Usage:
|
|
|
|
/// - Processing thread loops on `WaitForWork()` followed by processing all work in the queue
|
|
|
|
/// - Threads adding work first add their work to the queue, then call `NotifyOfWork()`
|
|
|
|
class WorkSema
|
|
|
|
{
|
|
|
|
/// Semaphore for sleeping the worker thread
|
|
|
|
KernelSemaphore m_sema;
|
|
|
|
/// Semaphore for sleeping thread waiting on worker queue empty
|
|
|
|
KernelSemaphore m_empty_sema;
|
|
|
|
/// Current state (see enum below)
|
|
|
|
std::atomic<s32> m_state{0};
|
|
|
|
|
|
|
|
// Expected call frequency is NotifyOfWork > WaitForWork > WaitForEmpty
|
|
|
|
// So optimize states for fast NotifyOfWork
|
|
|
|
enum
|
|
|
|
{
|
2022-03-27 04:56:00 +00:00
|
|
|
/* Any <-2 state: STATE_DEAD: Thread has crashed and is awaiting revival */
|
2022-03-25 10:12:52 +00:00
|
|
|
STATE_SPINNING = -2, ///< Worker thread is spinning waiting for work
|
|
|
|
STATE_SLEEPING = -1, ///< Worker thread is sleeping on m_sema
|
|
|
|
STATE_RUNNING_0 = 0, ///< Worker thread is processing work, but no work has been added since it last checked for new work
|
|
|
|
/* Any >0 state: STATE_RUNNING_N: Worker thread is processing work, and work has been added since it last checked for new work */
|
|
|
|
STATE_FLAG_WAITING_EMPTY = 1 << 30, ///< Flag to indicate that a thread is sleeping on m_empty_sema (can be applied to any STATE_RUNNING)
|
|
|
|
};
|
|
|
|
|
2022-03-27 04:56:00 +00:00
|
|
|
bool IsDead(s32 state)
|
|
|
|
{
|
|
|
|
return state < STATE_SPINNING;
|
|
|
|
}
|
|
|
|
|
2022-03-25 10:12:52 +00:00
|
|
|
bool IsReadyForSleep(s32 state)
|
|
|
|
{
|
|
|
|
s32 waiting_empty_cleared = state & (STATE_FLAG_WAITING_EMPTY - 1);
|
|
|
|
return waiting_empty_cleared == STATE_RUNNING_0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 NextStateWaitForWork(s32 current)
|
|
|
|
{
|
|
|
|
s32 new_state = IsReadyForSleep(current) ? STATE_SLEEPING : STATE_RUNNING_0;
|
|
|
|
return new_state | (current & STATE_FLAG_WAITING_EMPTY); // Preserve waiting empty flag for RUNNING_N → RUNNING_0
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// Notify the worker thread that you've added new work to its queue
|
|
|
|
void NotifyOfWork()
|
|
|
|
{
|
|
|
|
// State change:
|
2022-03-27 04:56:00 +00:00
|
|
|
// DEAD: Stay in DEAD (starting DEAD state is INT_MIN so we can assume we won't flip over to anything else)
|
2022-03-25 10:12:52 +00:00
|
|
|
// SPINNING: Change state to RUNNING. Thread will notice and process the new data
|
|
|
|
// SLEEPING: Change state to RUNNING and wake worker. Thread will wake up and process the new data.
|
|
|
|
// RUNNING_0: Change state to RUNNING_N.
|
|
|
|
// RUNNING_N: Stay in RUNNING_N
|
|
|
|
s32 old = m_state.fetch_add(2, std::memory_order_release);
|
|
|
|
if (old == STATE_SLEEPING)
|
|
|
|
m_sema.Post();
|
|
|
|
}
|
|
|
|
|
2022-05-15 08:19:03 +00:00
|
|
|
/// Checks if there's any work in the queue
|
|
|
|
bool CheckForWork();
|
2022-03-25 10:12:52 +00:00
|
|
|
/// Wait for work to be added to the queue
|
|
|
|
void WaitForWork();
|
|
|
|
/// Wait for work to be added to the queue, spinning for a bit before sleeping the thread
|
|
|
|
void WaitForWorkWithSpin();
|
2022-03-27 04:56:00 +00:00
|
|
|
/// Wait for the worker thread to finish processing all entries in the queue or die
|
|
|
|
/// Returns false if the thread is dead
|
|
|
|
bool WaitForEmpty();
|
|
|
|
/// Wait for the worker thread to finish processing all entries in the queue or die, spinning a bit before sleeping the thread
|
|
|
|
/// Returns false if the thread is dead
|
|
|
|
bool WaitForEmptyWithSpin();
|
|
|
|
/// Called by the worker thread to notify others of its death
|
|
|
|
/// Dead threads don't process work, and WaitForEmpty will return instantly even though there may be work in the queue
|
|
|
|
void Kill();
|
|
|
|
/// Reset the semaphore to the initial state
|
|
|
|
/// Should be called by the worker thread if it restarts after dying
|
|
|
|
void Reset();
|
2022-03-25 10:12:52 +00:00
|
|
|
};
|
2022-11-27 21:06:41 +00:00
|
|
|
|
|
|
|
/// A semaphore that definitely has a fast userspace path
|
|
|
|
class UserspaceSemaphore
|
|
|
|
{
|
|
|
|
KernelSemaphore m_sema;
|
2022-11-27 21:13:57 +00:00
|
|
|
std::atomic<int32_t> m_counter{0};
|
2022-11-27 21:06:41 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
UserspaceSemaphore() = default;
|
|
|
|
~UserspaceSemaphore() = default;
|
|
|
|
|
|
|
|
void Post()
|
|
|
|
{
|
|
|
|
if (m_counter.fetch_add(1, std::memory_order_release) < 0)
|
|
|
|
m_sema.Post();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wait()
|
|
|
|
{
|
|
|
|
if (m_counter.fetch_sub(1, std::memory_order_acquire) <= 0)
|
|
|
|
m_sema.Wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TryWait()
|
|
|
|
{
|
|
|
|
int32_t counter = m_counter.load(std::memory_order_relaxed);
|
|
|
|
while (counter > 0 && !m_counter.compare_exchange_weak(counter, counter - 1, std::memory_order_acquire, std::memory_order_relaxed))
|
|
|
|
;
|
|
|
|
return counter > 0;
|
|
|
|
}
|
|
|
|
};
|
2021-09-06 18:28:26 +00:00
|
|
|
} // namespace Threading
|