2009-10-17 18:21:30 +00:00
/* PCSX2 - PS2 Emulator for PCs
2010-05-03 14:08:02 +00:00
* Copyright ( C ) 2002 - 2010 PCSX2 Dev Team
2009-10-17 18:21:30 +00:00
*
* PCSX2 is free software : you can redistribute it and / or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* PCSX2 is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along with PCSX2 .
* If not , see < http : //www.gnu.org/licenses/>.
*/
2021-09-01 20:31:46 +00:00
# include "common/Threading.h"
# include "common/ThreadingInternal.h"
2009-10-17 18:21:30 +00:00
// --------------------------------------------------------------------------------------
// Semaphore Implementations
// --------------------------------------------------------------------------------------
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 ;
}
pxAssertDev ( ! ( value & STATE_FLAG_WAITING_EMPTY ) , " Multiple threads attempted to wait for empty (not currently supported) " ) ;
m_empty_sema . WaitWithYield ( ) ;
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 ) ;
}
pxAssertDev ( ! ( value & STATE_FLAG_WAITING_EMPTY ) , " Multiple threads attempted to wait for empty (not currently supported) " ) ;
m_empty_sema . WaitWithYield ( ) ;
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
}
# if !defined(__APPLE__) // macOS implementations are in DarwinSemaphore
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 ( )
{
pxAssertMsg ( ! wxThread : : IsMain ( ) , " Unyielding semaphore wait issued from the main/gui thread. Use WaitWithYield. " ) ;
# ifdef _WIN32
pthreadCancelableWait ( m_sema ) ;
# else
sem_wait ( & m_sema ) ;
# endif
}
void Threading : : KernelSemaphore : : WaitWithYield ( )
{
# if wxUSE_GUI
if ( ! wxThread : : IsMain ( ) | | ( wxTheApp = = NULL ) )
{
Wait ( ) ;
}
else
{
# ifdef _WIN32
u64 millis = def_yieldgui_interval . GetMilliseconds ( ) . GetValue ( ) ;
2022-04-15 13:07:54 +00:00
while ( pthreadCancelableTimedWait ( m_sema , millis ) = = ETIMEDOUT )
2022-03-25 10:12:52 +00:00
YieldToMain ( ) ;
# else
while ( true )
{
wxDateTime megafail ( wxDateTime : : UNow ( ) + def_yieldgui_interval ) ;
const timespec fail = { megafail . GetTicks ( ) , megafail . GetMillisecond ( ) * 1000000 } ;
if ( sem_timedwait ( & m_sema , & fail ) = = 0 )
break ;
YieldToMain ( ) ;
}
# endif
}
# else
Wait ( ) ;
# endif
}
2009-10-17 18:21:30 +00:00
Threading : : Semaphore : : Semaphore ( )
{
2021-09-06 18:28:26 +00:00
sem_init ( & m_sema , false , 0 ) ;
2009-10-17 18:21:30 +00:00
}
2017-05-06 12:22:00 +00:00
Threading : : Semaphore : : ~ Semaphore ( )
2009-10-17 18:21:30 +00:00
{
2021-09-06 18:28:26 +00:00
sem_destroy ( & m_sema ) ;
2009-10-17 18:21:30 +00:00
}
void Threading : : Semaphore : : Reset ( )
{
2021-09-06 18:28:26 +00:00
sem_destroy ( & m_sema ) ;
sem_init ( & m_sema , false , 0 ) ;
2009-10-17 18:21:30 +00:00
}
void Threading : : Semaphore : : Post ( )
{
2021-09-06 18:28:26 +00:00
sem_post ( & m_sema ) ;
2009-10-17 18:21:30 +00:00
}
2016-11-12 15:28:37 +00:00
void Threading : : Semaphore : : Post ( int multiple )
2009-10-17 18:21:30 +00:00
{
# if defined(_MSC_VER)
2021-09-06 18:28:26 +00:00
sem_post_multiple ( & m_sema , multiple ) ;
2009-10-17 18:21:30 +00:00
# else
2021-09-06 18:28:26 +00:00
// Only w32pthreads has the post_multiple, but it's easy enough to fake:
while ( multiple > 0 )
{
multiple - - ;
sem_post ( & m_sema ) ;
}
2009-10-17 18:21:30 +00:00
# endif
}
2009-11-01 09:27:16 +00:00
void Threading : : Semaphore : : WaitWithoutYield ( )
2009-10-17 18:21:30 +00:00
{
2021-09-06 18:28:26 +00:00
pxAssertMsg ( ! wxThread : : IsMain ( ) , " Unyielding semaphore wait issued from the main/gui thread. Please use Wait() instead. " ) ;
sem_wait ( & m_sema ) ;
2009-10-17 18:21:30 +00:00
}
2021-09-06 18:28:26 +00:00
bool Threading : : Semaphore : : WaitWithoutYield ( const wxTimeSpan & timeout )
2009-10-17 18:21:30 +00:00
{
2021-09-06 18:28:26 +00:00
wxDateTime megafail ( wxDateTime : : UNow ( ) + timeout ) ;
const timespec fail = { megafail . GetTicks ( ) , megafail . GetMillisecond ( ) * 1000000 } ;
return sem_timedwait ( & m_sema , & fail ) = = 0 ;
2009-10-17 18:21:30 +00:00
}
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
// user input continues to be handled and that windoes continue to repaint. If the Wait is
// called from another thread, no message pumping is performed.
//
void Threading : : Semaphore : : Wait ( )
{
# if wxUSE_GUI
2021-09-06 18:28:26 +00:00
if ( ! wxThread : : IsMain ( ) | | ( wxTheApp = = NULL ) )
{
sem_wait ( & m_sema ) ;
}
else if ( _WaitGui_RecursionGuard ( L " Semaphore::Wait " ) )
{
sem_wait ( & m_sema ) ;
}
else
{
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
while ( ! WaitWithoutYield ( def_yieldgui_interval ) )
YieldToMain ( ) ;
}
2009-10-17 18:21:30 +00:00
# else
2021-09-06 18:28:26 +00:00
sem_wait ( & m_sema ) ;
2009-10-17 18:21:30 +00:00
# endif
}
2009-11-01 09:27:16 +00:00
// This is a wxApp-safe implementation of WaitWithoutYield, which makes sure and executes the App's
2009-10-17 18:21:30 +00:00
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
// user input continues to be handled and that windows continue to repaint. If the Wait is
// called from another thread, no message pumping is performed.
//
// Returns:
// false if the wait timed out before the semaphore was signaled, or true if the signal was
// reached prior to timeout.
//
2021-09-06 18:28:26 +00:00
bool Threading : : Semaphore : : Wait ( const wxTimeSpan & timeout )
2009-10-17 18:21:30 +00:00
{
# if wxUSE_GUI
2021-09-06 18:28:26 +00:00
if ( ! wxThread : : IsMain ( ) | | ( wxTheApp = = NULL ) )
{
return WaitWithoutYield ( timeout ) ;
}
else if ( _WaitGui_RecursionGuard ( L " Semaphore::TimedWait " ) )
{
return WaitWithoutYield ( timeout ) ;
}
else
{
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
wxTimeSpan countdown ( ( timeout ) ) ;
do
{
if ( WaitWithoutYield ( def_yieldgui_interval ) )
break ;
YieldToMain ( ) ;
countdown - = def_yieldgui_interval ;
} while ( countdown . GetMilliseconds ( ) > 0 ) ;
return countdown . GetMilliseconds ( ) > 0 ;
}
2009-10-17 18:21:30 +00:00
# else
2021-09-06 18:28:26 +00:00
return WaitWithoutYield ( timeout ) ;
2009-10-17 18:21:30 +00:00
# endif
}
// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state
// after the wait has completed. Useful for situations where the semaphore itself is stored on
// the stack and passed to another thread via GUI message or such, avoiding complications where
// the thread might be canceled and the stack value becomes invalid.
//
2009-11-01 09:27:16 +00:00
// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitWithoutYield(), so
// consider manually specifying the thread as uncancellable and using WaitWithoutYield() instead if you need
2009-10-17 18:21:30 +00:00
// to do a lot of no-cancel waits in a tight loop worker thread, for example.
void Threading : : Semaphore : : WaitNoCancel ( )
{
2021-09-06 18:28:26 +00:00
int oldstate ;
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE , & oldstate ) ;
//WaitWithoutYield();
Wait ( ) ;
pthread_setcancelstate ( oldstate , NULL ) ;
2009-10-17 18:21:30 +00:00
}
2021-09-06 18:28:26 +00:00
void Threading : : Semaphore : : WaitNoCancel ( const wxTimeSpan & timeout )
2009-10-21 08:57:05 +00:00
{
2021-09-06 18:28:26 +00:00
int oldstate ;
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE , & oldstate ) ;
//WaitWithoutYield( timeout );
Wait ( timeout ) ;
pthread_setcancelstate ( oldstate , NULL ) ;
2009-10-21 08:57:05 +00:00
}
2021-03-28 08:09:52 +00:00
bool Threading : : Semaphore : : TryWait ( )
{
return sem_trywait ( & m_sema ) = = 0 ;
}
2009-10-17 18:21:30 +00:00
int Threading : : Semaphore : : Count ( )
{
2021-09-06 18:28:26 +00:00
int retval ;
sem_getvalue ( & m_sema , & retval ) ;
return retval ;
2009-10-17 18:21:30 +00:00
}
2021-09-03 10:43:33 +00:00
# endif