2023-12-22 11:57:49 +00:00
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
2009-10-17 18:21:30 +00:00
2021-09-01 20:31:46 +00:00
# include "common/Threading.h"
2022-05-09 10:28:57 +00:00
# include "common/Assertions.h"
2023-12-26 11:25:10 +00:00
# include "common/HostSys.h"
2022-05-05 13:01:14 +00:00
# ifdef _WIN32
# include "common/RedtapeWindows.h"
# endif
2009-10-17 18:21:30 +00:00
2022-05-09 10:28:57 +00:00
# include <limits>
2009-10-17 18:21:30 +00:00
// --------------------------------------------------------------------------------------
// Semaphore Implementations
// --------------------------------------------------------------------------------------
2022-05-15 08:19:03 +00:00
bool Threading : : WorkSema : : CheckForWork ( )
{
s32 value = m_state . load ( std : : memory_order_relaxed ) ;
pxAssert ( ! IsDead ( value ) ) ;
// we want to switch to the running state, but preserve the waiting empty bit for RUNNING_N -> RUNNING_0
// otherwise, we clear the waiting flag (since we're notifying the waiter that we're empty below)
while ( ! m_state . compare_exchange_weak ( value ,
IsReadyForSleep ( value ) ? STATE_RUNNING_0 : ( value & STATE_FLAG_WAITING_EMPTY ) ,
std : : memory_order_acq_rel , std : : memory_order_relaxed ) )
{
}
// if we're not empty, we have work to do
if ( ! IsReadyForSleep ( value ) )
return true ;
// this means we're empty, so notify any waiters
if ( value & STATE_FLAG_WAITING_EMPTY )
m_empty_sema . Post ( ) ;
// no work to do
return false ;
}
2022-03-25 10:12:52 +00:00
void Threading : : WorkSema : : WaitForWork ( )
{
// State change:
// SLEEPING, SPINNING: This is the worker thread and it's clearly not asleep or spinning, so these states should be impossible
// RUNNING_0: Change state to SLEEPING, wake up thread if WAITING_EMPTY
// RUNNING_N: Change state to RUNNING_0 (and preserve WAITING_EMPTY flag)
s32 value = m_state . load ( std : : memory_order_relaxed ) ;
2022-03-27 04:56:00 +00:00
pxAssert ( ! IsDead ( value ) ) ;
2022-03-25 10:12:52 +00:00
while ( ! m_state . compare_exchange_weak ( value , NextStateWaitForWork ( value ) , std : : memory_order_acq_rel , std : : memory_order_relaxed ) )
;
if ( IsReadyForSleep ( value ) )
{
if ( value & STATE_FLAG_WAITING_EMPTY )
m_empty_sema . Post ( ) ;
m_sema . Wait ( ) ;
// Acknowledge any additional work added between wake up request and getting here
m_state . fetch_and ( STATE_FLAG_WAITING_EMPTY , std : : memory_order_acquire ) ;
}
}
void Threading : : WorkSema : : WaitForWorkWithSpin ( )
{
s32 value = m_state . load ( std : : memory_order_relaxed ) ;
2022-03-27 04:56:00 +00:00
pxAssert ( ! IsDead ( value ) ) ;
2022-03-25 10:12:52 +00:00
while ( IsReadyForSleep ( value ) )
{
if ( m_state . compare_exchange_weak ( value , STATE_SPINNING , std : : memory_order_release , std : : memory_order_relaxed ) )
{
if ( value & STATE_FLAG_WAITING_EMPTY )
m_empty_sema . Post ( ) ;
value = STATE_SPINNING ;
break ;
}
}
u32 waited = 0 ;
while ( value < 0 )
{
if ( waited > SPIN_TIME_NS )
{
if ( ! m_state . compare_exchange_weak ( value , STATE_SLEEPING , std : : memory_order_relaxed ) )
continue ;
m_sema . Wait ( ) ;
break ;
}
waited + = ShortSpin ( ) ;
value = m_state . load ( std : : memory_order_relaxed ) ;
}
// Clear back to STATE_RUNNING_0 (but preserve waiting empty flag)
m_state . fetch_and ( STATE_FLAG_WAITING_EMPTY , std : : memory_order_acquire ) ;
}
2022-03-27 04:56:00 +00:00
bool Threading : : WorkSema : : WaitForEmpty ( )
2022-03-25 10:12:52 +00:00
{
s32 value = m_state . load ( std : : memory_order_acquire ) ;
while ( true )
{
if ( value < 0 )
2022-03-27 04:56:00 +00:00
return ! IsDead ( value ) ; // STATE_SLEEPING or STATE_SPINNING, queue is empty!
2022-04-16 05:28:37 +00:00
// Note: We technically only need memory_order_acquire on *failure* (because that's when we could leave without sleeping), but libstdc++ still asserts on failure < success
if ( m_state . compare_exchange_weak ( value , value | STATE_FLAG_WAITING_EMPTY , std : : memory_order_acquire ) )
2022-03-25 10:12:52 +00:00
break ;
}
2023-12-22 10:30:31 +00:00
pxAssertMsg ( ! ( value & STATE_FLAG_WAITING_EMPTY ) , " Multiple threads attempted to wait for empty (not currently supported) " ) ;
2022-05-05 13:01:12 +00:00
m_empty_sema . Wait ( ) ;
2022-03-27 04:56:00 +00:00
return ! IsDead ( m_state . load ( std : : memory_order_relaxed ) ) ;
2022-03-25 10:12:52 +00:00
}
2022-03-27 04:56:00 +00:00
bool Threading : : WorkSema : : WaitForEmptyWithSpin ( )
2022-03-25 10:12:52 +00:00
{
s32 value = m_state . load ( std : : memory_order_acquire ) ;
u32 waited = 0 ;
while ( true )
{
if ( value < 0 )
2022-03-27 04:56:00 +00:00
return ! IsDead ( value ) ; // STATE_SLEEPING or STATE_SPINNING, queue is empty!
2022-04-16 05:28:37 +00:00
if ( waited > SPIN_TIME_NS & & m_state . compare_exchange_weak ( value , value | STATE_FLAG_WAITING_EMPTY , std : : memory_order_acquire ) )
2022-03-25 10:12:52 +00:00
break ;
waited + = ShortSpin ( ) ;
value = m_state . load ( std : : memory_order_acquire ) ;
}
2023-12-22 10:30:31 +00:00
pxAssertMsg ( ! ( value & STATE_FLAG_WAITING_EMPTY ) , " Multiple threads attempted to wait for empty (not currently supported) " ) ;
2022-05-05 13:01:12 +00:00
m_empty_sema . Wait ( ) ;
2022-03-27 04:56:00 +00:00
return ! IsDead ( m_state . load ( std : : memory_order_relaxed ) ) ;
}
void Threading : : WorkSema : : Kill ( )
{
s32 value = m_state . exchange ( std : : numeric_limits < s32 > : : min ( ) , std : : memory_order_release ) ;
if ( value & STATE_FLAG_WAITING_EMPTY )
m_empty_sema . Post ( ) ;
}
void Threading : : WorkSema : : Reset ( )
{
m_state = STATE_RUNNING_0 ;
2022-03-25 10:12:52 +00:00
}
2024-05-29 10:30:53 +00:00
# if !defined(__APPLE__) // macOS implementations are in DarwinThreads
2022-03-25 10:12:52 +00:00
Threading : : KernelSemaphore : : KernelSemaphore ( )
{
# ifdef _WIN32
m_sema = CreateSemaphore ( nullptr , 0 , LONG_MAX , nullptr ) ;
# else
sem_init ( & m_sema , false , 0 ) ;
# endif
}
Threading : : KernelSemaphore : : ~ KernelSemaphore ( )
{
# ifdef _WIN32
CloseHandle ( m_sema ) ;
# else
sem_destroy ( & m_sema ) ;
# endif
}
void Threading : : KernelSemaphore : : Post ( )
{
# ifdef _WIN32
ReleaseSemaphore ( m_sema , 1 , nullptr ) ;
# else
sem_post ( & m_sema ) ;
# endif
}
void Threading : : KernelSemaphore : : Wait ( )
{
# ifdef _WIN32
2022-05-05 13:01:12 +00:00
WaitForSingleObject ( m_sema , INFINITE ) ;
2022-03-25 10:12:52 +00:00
# else
sem_wait ( & m_sema ) ;
# endif
}
2022-05-05 13:01:12 +00:00
bool Threading : : KernelSemaphore : : TryWait ( )
2022-03-25 10:12:52 +00:00
{
# ifdef _WIN32
2022-05-05 13:01:12 +00:00
return WaitForSingleObject ( m_sema , 0 ) = = WAIT_OBJECT_0 ;
2022-03-25 10:12:52 +00:00
# else
2022-05-05 13:01:12 +00:00
return sem_trywait ( & m_sema ) = = 0 ;
2022-03-25 10:12:52 +00:00
# endif
}
2022-05-09 10:28:57 +00:00
# endif