2012-11-14 23:39:56 +00:00
# pragma once
2014-12-24 22:24:17 +00:00
# include "Emu/Memory/atomic_type.h"
2012-11-14 23:39:56 +00:00
2014-07-11 11:59:13 +00:00
static std : : thread : : id main_thread ;
2014-01-31 18:40:18 +00:00
class NamedThreadBase
2012-11-14 23:39:56 +00:00
{
2013-11-27 19:16:19 +00:00
std : : string m_name ;
2014-06-20 11:00:36 +00:00
std : : condition_variable m_signal_cv ;
std : : mutex m_signal_mtx ;
2013-06-30 08:46:29 +00:00
public :
2014-09-14 22:17:24 +00:00
std : : atomic < bool > m_tls_assigned ;
NamedThreadBase ( const std : : string & name ) : m_name ( name ) , m_tls_assigned ( false )
2014-01-31 18:40:18 +00:00
{
}
2012-11-14 23:39:56 +00:00
2014-09-14 22:17:24 +00:00
NamedThreadBase ( ) : m_tls_assigned ( false )
2014-01-31 18:40:18 +00:00
{
}
2013-06-30 08:46:29 +00:00
2013-11-27 19:16:19 +00:00
virtual std : : string GetThreadName ( ) const ;
virtual void SetThreadName ( const std : : string & name ) ;
2014-06-20 11:00:36 +00:00
2014-09-12 19:27:33 +00:00
void WaitForAnySignal ( u64 time = 1 ) ;
2014-06-20 11:00:36 +00:00
2014-08-25 18:09:48 +00:00
void Notify ( ) ;
2012-11-14 23:39:56 +00:00
} ;
2014-01-31 18:40:18 +00:00
NamedThreadBase * GetCurrentNamedThread ( ) ;
2014-08-20 14:23:48 +00:00
void SetCurrentNamedThread ( NamedThreadBase * value ) ;
2013-06-30 08:46:29 +00:00
2014-01-31 18:40:18 +00:00
class ThreadBase : public NamedThreadBase
2012-11-14 23:39:56 +00:00
{
2014-01-31 18:40:18 +00:00
protected :
std : : atomic < bool > m_destroy ;
std : : atomic < bool > m_alive ;
std : : thread * m_executor ;
2012-11-14 23:39:56 +00:00
2014-01-31 18:40:18 +00:00
mutable std : : mutex m_main_mutex ;
2013-06-30 08:46:29 +00:00
2014-01-31 18:40:18 +00:00
ThreadBase ( const std : : string & name ) ;
~ ThreadBase ( ) ;
2012-11-14 23:39:56 +00:00
2014-01-31 18:40:18 +00:00
public :
void Start ( ) ;
2014-02-14 19:50:02 +00:00
void Stop ( bool wait = true , bool send_destroy = true ) ;
2013-06-30 08:46:29 +00:00
2014-01-31 18:40:18 +00:00
bool Join ( ) const ;
bool IsAlive ( ) const ;
bool TestDestroy ( ) const ;
2012-11-14 23:39:56 +00:00
2014-01-31 18:40:18 +00:00
virtual void Task ( ) = 0 ;
2012-11-14 23:39:56 +00:00
} ;
2014-01-31 18:40:18 +00:00
class thread
{
std : : string m_name ;
std : : thread m_thr ;
public :
thread ( const std : : string & name , std : : function < void ( ) > func ) ;
thread ( const std : : string & name ) ;
thread ( ) ;
2014-02-19 17:27:52 +00:00
public :
2014-01-31 18:40:18 +00:00
void start ( std : : function < void ( ) > func ) ;
void detach ( ) ;
void join ( ) ;
bool joinable ( ) const ;
2014-10-10 21:33:57 +00:00
} ;
2014-10-17 20:13:25 +00:00
class slw_mutex_t
{
} ;
class slw_recursive_mutex_t
{
} ;
class slw_shared_mutex_t
{
} ;
2014-10-16 16:29:41 +00:00
class waiter_map_t
2014-10-10 22:37:20 +00:00
{
2014-10-16 16:29:41 +00:00
// TODO: optimize (use custom lightweight readers-writer lock)
std : : mutex m_mutex ;
2014-10-10 22:37:20 +00:00
2014-10-16 16:29:41 +00:00
struct waiter_t
{
u64 signal_id ;
NamedThreadBase * thread ;
} ;
2014-10-10 22:37:20 +00:00
2014-10-16 16:29:41 +00:00
std : : vector < waiter_t > m_waiters ;
std : : string m_name ;
2014-10-10 21:33:57 +00:00
2014-10-16 16:29:41 +00:00
struct waiter_reg_t
{
2014-10-17 20:13:25 +00:00
NamedThreadBase * thread ;
2014-10-16 16:29:41 +00:00
const u64 signal_id ;
waiter_map_t & map ;
2014-10-17 20:13:25 +00:00
waiter_reg_t ( waiter_map_t & map , u64 signal_id )
: thread ( nullptr )
, signal_id ( signal_id )
, map ( map )
{
}
2014-10-16 16:29:41 +00:00
~ waiter_reg_t ( ) ;
2014-10-17 20:13:25 +00:00
void init ( ) ;
2014-10-16 16:29:41 +00:00
} ;
bool is_stopped ( u64 signal_id ) ;
2014-10-10 21:33:57 +00:00
2014-10-16 16:29:41 +00:00
public :
2014-10-17 20:13:25 +00:00
waiter_map_t ( const char * name )
: m_name ( name )
{
}
2014-10-16 16:29:41 +00:00
// wait until waiter_func() returns true, signal_id is an arbitrary number
2014-10-16 19:34:17 +00:00
template < typename WT > __forceinline void wait_op ( u64 signal_id , const WT waiter_func )
2014-10-10 21:33:57 +00:00
{
2014-10-16 16:29:41 +00:00
// register waiter
waiter_reg_t waiter ( * this , signal_id ) ;
2014-12-22 23:31:11 +00:00
// check the condition or if the emulator is stopped
2014-10-17 20:13:25 +00:00
while ( ! waiter_func ( ) & & ! is_stopped ( signal_id ) )
2014-10-16 16:29:41 +00:00
{
2014-12-22 23:31:11 +00:00
// initialize waiter (only once)
2014-10-17 20:13:25 +00:00
waiter . init ( ) ;
2014-10-16 16:29:41 +00:00
// wait for 1 ms or until signal arrived
waiter . thread - > WaitForAnySignal ( 1 ) ;
}
2014-10-10 21:33:57 +00:00
}
2014-10-16 16:29:41 +00:00
// signal all threads waiting on waiter_op() with the same signal_id (signaling only hints those threads that corresponding conditions are *probably* met)
void notify ( u64 signal_id ) ;
} ;
2014-12-24 22:24:17 +00:00
2014-12-25 20:30:34 +00:00
bool squeue_test_exit ( const volatile bool * do_exit ) ;
2014-12-24 22:24:17 +00:00
template < typename T , u32 sq_size = 256 >
class squeue_t
{
struct squeue_sync_var_t
{
struct
{
u32 position : 31 ;
u32 read_lock : 1 ;
} ;
struct
{
u32 count : 31 ;
u32 write_lock : 1 ;
} ;
} ;
atomic_le_t < squeue_sync_var_t > m_sync ;
mutable std : : mutex m_rcv_mutex , m_wcv_mutex ;
mutable std : : condition_variable m_rcv , m_wcv ;
T m_data [ sq_size ] ;
public :
squeue_t ( )
{
m_sync . write_relaxed ( { } ) ;
}
u32 get_max_size ( ) const
{
return sq_size ;
}
bool is_full ( ) const volatile
{
return m_sync . read_relaxed ( ) . count = = sq_size ;
}
bool push ( const T & data , const volatile bool * do_exit = nullptr )
{
u32 pos = 0 ;
while ( ! m_sync . atomic_op_sync ( true , [ & pos ] ( squeue_sync_var_t & sync ) - > bool
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
if ( sync . write_lock | | sync . count = = sq_size )
{
return false ;
}
sync . write_lock = 1 ;
pos = sync . position + sync . count ;
return true ;
} ) )
{
2014-12-25 20:30:34 +00:00
if ( squeue_test_exit ( do_exit ) )
2014-12-24 22:24:17 +00:00
{
return false ;
}
std : : unique_lock < std : : mutex > wcv_lock ( m_wcv_mutex ) ;
m_wcv . wait_for ( wcv_lock , std : : chrono : : milliseconds ( 1 ) ) ;
}
m_data [ pos > = sq_size ? pos - sq_size : pos ] = data ;
m_sync . atomic_op ( [ ] ( squeue_sync_var_t & sync )
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
assert ( sync . write_lock ) ;
sync . write_lock = 0 ;
sync . count + + ;
} ) ;
m_rcv . notify_one ( ) ;
m_wcv . notify_one ( ) ;
return true ;
}
bool try_push ( const T & data )
{
static const volatile bool no_wait = true ;
return push ( data , & no_wait ) ;
}
bool pop ( T & data , const volatile bool * do_exit = nullptr )
{
u32 pos = 0 ;
while ( ! m_sync . atomic_op_sync ( true , [ & pos ] ( squeue_sync_var_t & sync ) - > bool
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
if ( sync . read_lock | | ! sync . count )
{
return false ;
}
sync . read_lock = 1 ;
pos = sync . position ;
return true ;
} ) )
{
2014-12-25 20:30:34 +00:00
if ( squeue_test_exit ( do_exit ) )
2014-12-24 22:24:17 +00:00
{
return false ;
}
std : : unique_lock < std : : mutex > rcv_lock ( m_rcv_mutex ) ;
m_rcv . wait_for ( rcv_lock , std : : chrono : : milliseconds ( 1 ) ) ;
}
data = m_data [ pos ] ;
m_sync . atomic_op ( [ ] ( squeue_sync_var_t & sync )
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
assert ( sync . read_lock ) ;
sync . read_lock = 0 ;
sync . position + + ;
sync . count - - ;
if ( sync . position = = sq_size )
{
sync . position = 0 ;
}
} ) ;
m_rcv . notify_one ( ) ;
m_wcv . notify_one ( ) ;
return true ;
}
bool try_pop ( T & data )
{
static const volatile bool no_wait = true ;
return pop ( data , & no_wait ) ;
}
void clear ( )
{
while ( ! m_sync . atomic_op_sync ( true , [ ] ( squeue_sync_var_t & sync ) - > bool
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
if ( sync . read_lock | | sync . write_lock )
{
return false ;
}
sync . read_lock = 1 ;
sync . write_lock = 1 ;
return true ;
} ) )
{
std : : unique_lock < std : : mutex > rcv_lock ( m_rcv_mutex ) ;
m_rcv . wait_for ( rcv_lock , std : : chrono : : milliseconds ( 1 ) ) ;
}
m_sync . exchange ( { } ) ;
m_wcv . notify_one ( ) ;
m_rcv . notify_one ( ) ;
}
bool peek ( T & data , u32 start_pos = 0 , const volatile bool * do_exit = nullptr )
{
assert ( start_pos < sq_size ) ;
u32 pos = 0 ;
while ( ! m_sync . atomic_op_sync ( true , [ & pos , start_pos ] ( squeue_sync_var_t & sync ) - > bool
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
if ( sync . read_lock | | sync . count < = start_pos )
{
return false ;
}
sync . read_lock = 1 ;
pos = sync . position + start_pos ;
return true ;
} ) )
{
2014-12-25 20:30:34 +00:00
if ( squeue_test_exit ( do_exit ) )
2014-12-24 22:24:17 +00:00
{
return false ;
}
std : : unique_lock < std : : mutex > rcv_lock ( m_rcv_mutex ) ;
m_rcv . wait_for ( rcv_lock , std : : chrono : : milliseconds ( 1 ) ) ;
}
data = m_data [ pos > = sq_size ? pos - sq_size : pos ] ;
m_sync . atomic_op ( [ ] ( squeue_sync_var_t & sync )
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
assert ( sync . read_lock ) ;
sync . read_lock = 0 ;
} ) ;
m_rcv . notify_one ( ) ;
return true ;
}
bool try_peek ( T & data , u32 start_pos = 0 )
{
static const volatile bool no_wait = true ;
return peek ( data , start_pos , & no_wait ) ;
}
} ;