2012-11-15 01:39:56 +02:00
# pragma once
2014-07-11 21:59:13 +10:00
static std : : thread : : id main_thread ;
2014-01-31 20:40:18 +02:00
class NamedThreadBase
2012-11-15 01:39:56 +02:00
{
2013-11-28 05:16:19 +10:00
std : : string m_name ;
2014-06-20 15:00:36 +04:00
std : : condition_variable m_signal_cv ;
std : : mutex m_signal_mtx ;
2013-06-30 11:46:29 +03:00
public :
2014-09-15 02:17:24 +04:00
std : : atomic < bool > m_tls_assigned ;
NamedThreadBase ( const std : : string & name ) : m_name ( name ) , m_tls_assigned ( false )
2014-01-31 20:40:18 +02:00
{
}
2012-11-15 01:39:56 +02:00
2014-09-15 02:17:24 +04:00
NamedThreadBase ( ) : m_tls_assigned ( false )
2014-01-31 20:40:18 +02:00
{
}
2013-06-30 11:46:29 +03:00
2013-11-28 05:16:19 +10:00
virtual std : : string GetThreadName ( ) const ;
virtual void SetThreadName ( const std : : string & name ) ;
2014-06-20 15:00:36 +04:00
2014-09-12 23:27:33 +04:00
void WaitForAnySignal ( u64 time = 1 ) ;
2014-08-25 22:09:48 +04:00
void Notify ( ) ;
2015-02-18 19:22:06 +03:00
virtual void DumpInformation ( ) { }
2012-11-15 01:39:56 +02:00
} ;
2014-01-31 20:40:18 +02:00
NamedThreadBase * GetCurrentNamedThread ( ) ;
2014-08-20 18:23:48 +04:00
void SetCurrentNamedThread ( NamedThreadBase * value ) ;
2013-06-30 11:46:29 +03:00
2014-01-31 20:40:18 +02:00
class ThreadBase : public NamedThreadBase
2012-11-15 01:39:56 +02:00
{
2014-01-31 20:40:18 +02:00
protected :
std : : atomic < bool > m_destroy ;
std : : atomic < bool > m_alive ;
std : : thread * m_executor ;
2012-11-15 01:39:56 +02:00
2014-01-31 20:40:18 +02:00
mutable std : : mutex m_main_mutex ;
2013-06-30 11:46:29 +03:00
2014-01-31 20:40:18 +02:00
ThreadBase ( const std : : string & name ) ;
~ ThreadBase ( ) ;
2012-11-15 01:39:56 +02:00
2014-01-31 20:40:18 +02:00
public :
void Start ( ) ;
2014-02-14 20:50:02 +01:00
void Stop ( bool wait = true , bool send_destroy = true ) ;
2013-06-30 11:46:29 +03:00
2014-01-31 20:40:18 +02:00
bool Join ( ) const ;
bool IsAlive ( ) const ;
bool TestDestroy ( ) const ;
2012-11-15 01:39:56 +02:00
2014-01-31 20:40:18 +02:00
virtual void Task ( ) = 0 ;
2012-11-15 01:39:56 +02:00
} ;
2015-01-16 17:36:53 +03:00
class thread_t
2014-01-31 20:40:18 +02:00
{
2015-01-16 17:36:53 +03:00
enum thread_state_t
{
TS_NON_EXISTENT ,
TS_JOINABLE ,
} ;
std : : atomic < thread_state_t > m_state ;
2014-01-31 20:40:18 +02:00
std : : string m_name ;
std : : thread m_thr ;
2015-01-17 19:14:58 +03:00
bool m_autojoin ;
2014-01-31 20:40:18 +02:00
public :
2015-01-17 19:14:58 +03:00
thread_t ( const std : : string & name , bool autojoin , std : : function < void ( ) > func ) ;
2015-01-16 17:36:53 +03:00
thread_t ( const std : : string & name , std : : function < void ( ) > func ) ;
thread_t ( const std : : string & name ) ;
thread_t ( ) ;
~ thread_t ( ) ;
thread_t ( const thread_t & right ) = delete ;
thread_t ( thread_t & & right ) = delete ;
2014-01-31 20:40:18 +02:00
2015-01-16 17:36:53 +03:00
thread_t & operator = ( const thread_t & right ) = delete ;
thread_t & operator = ( thread_t & & right ) = delete ;
2014-02-19 19:27:52 +02:00
public :
2015-01-16 17:36:53 +03:00
void set_name ( const std : : string & name ) ;
2014-01-31 20:40:18 +02:00
void start ( std : : function < void ( ) > func ) ;
void detach ( ) ;
void join ( ) ;
bool joinable ( ) const ;
2014-10-11 01:33:57 +04:00
} ;
2014-10-18 00:13:25 +04:00
class slw_mutex_t
{
} ;
class slw_recursive_mutex_t
{
} ;
class slw_shared_mutex_t
{
} ;
2015-03-07 19:03:42 +03:00
struct waiter_map_t
2014-10-11 02:37:20 +04:00
{
2015-03-07 19:03:42 +03:00
static const size_t size = 32 ;
2014-10-11 02:37:20 +04:00
2015-03-07 19:03:42 +03:00
std : : array < std : : mutex , size > mutex ;
std : : array < std : : condition_variable , size > cv ;
2014-10-18 00:13:25 +04:00
2015-03-07 19:03:42 +03:00
const std : : string name ;
2014-10-11 01:33:57 +04:00
2014-10-18 00:13:25 +04:00
waiter_map_t ( const char * name )
2015-03-07 19:03:42 +03:00
: name ( name )
2014-10-18 00:13:25 +04:00
{
}
2014-10-16 20:29:41 +04:00
2015-03-07 19:03:42 +03:00
bool is_stopped ( u64 signal_id ) ;
2014-10-16 20:29:41 +04:00
// wait until waiter_func() returns true, signal_id is an arbitrary number
2015-03-07 19:03:42 +03:00
template < typename S , typename WT > __forceinline __safebuffers void wait_op ( const S & signal_id , const WT waiter_func )
2014-10-11 01:33:57 +04:00
{
2015-03-07 19:03:42 +03:00
// generate hash
const auto hash = std : : hash < S > ( ) ( signal_id ) % size ;
// set mutex locker
std : : unique_lock < std : : mutex > locker ( mutex [ hash ] , std : : defer_lock ) ;
2014-10-16 20:29:41 +04:00
2014-12-23 02:31:11 +03:00
// check the condition or if the emulator is stopped
2014-10-18 00:13:25 +04:00
while ( ! waiter_func ( ) & & ! is_stopped ( signal_id ) )
2014-10-16 20:29:41 +04:00
{
2015-03-07 19:03:42 +03:00
// lock the mutex and initialize waiter (only once)
if ( ! locker . owns_lock ( ) )
{
locker . lock ( ) ;
}
// wait on appropriate condition variable for 1 ms or until signal arrived
cv [ hash ] . wait_for ( locker , std : : chrono : : milliseconds ( 1 ) ) ;
2014-10-16 20:29:41 +04:00
}
2014-10-11 01:33:57 +04:00
}
2014-10-16 20:29:41 +04:00
// signal all threads waiting on waiter_op() with the same signal_id (signaling only hints those threads that corresponding conditions are *probably* met)
2015-03-07 19:03:42 +03:00
template < typename S > __forceinline void notify ( const S & signal_id )
{
// generate hash
const auto hash = std : : hash < S > ( ) ( signal_id ) % size ;
// signal appropriate condition variable
cv [ hash ] . notify_all ( ) ;
}
2014-10-16 20:29:41 +04:00
} ;
2014-12-25 01:24:17 +03:00
2015-01-17 19:14:58 +03:00
extern const std : : function < bool ( ) > SQUEUE_ALWAYS_EXIT ;
extern const std : : function < bool ( ) > SQUEUE_NEVER_EXIT ;
2015-01-16 20:09:53 +03:00
bool squeue_test_exit ( ) ;
2014-12-25 23:30:34 +03:00
2014-12-25 01:24:17 +03:00
template < typename T , u32 sq_size = 256 >
class squeue_t
{
struct squeue_sync_var_t
{
struct
{
u32 position : 31 ;
2014-12-26 01:58:43 +03:00
u32 pop_lock : 1 ;
2014-12-25 01:24:17 +03:00
} ;
struct
{
u32 count : 31 ;
2014-12-26 01:58:43 +03:00
u32 push_lock : 1 ;
2014-12-25 01:24:17 +03:00
} ;
} ;
atomic_le_t < squeue_sync_var_t > m_sync ;
2014-12-28 16:15:22 +03:00
mutable std : : mutex m_rcv_mutex ;
mutable std : : mutex m_wcv_mutex ;
mutable std : : condition_variable m_rcv ;
mutable std : : condition_variable m_wcv ;
2014-12-25 01:24:17 +03:00
T m_data [ sq_size ] ;
2014-12-26 01:49:55 +03:00
enum squeue_sync_var_result : u32
{
SQSVR_OK = 0 ,
SQSVR_LOCKED = 1 ,
SQSVR_FAILED = 2 ,
} ;
2014-12-25 01:24:17 +03:00
public :
squeue_t ( )
2015-03-13 04:09:53 +03:00
: m_sync ( { } )
2014-12-25 01:24:17 +03:00
{
}
u32 get_max_size ( ) const
{
return sq_size ;
}
2015-03-13 04:09:53 +03:00
bool is_full ( ) const volatile
2014-12-25 01:24:17 +03:00
{
2015-03-13 04:09:53 +03:00
return m_sync . data . count = = sq_size ;
2014-12-25 01:24:17 +03:00
}
2015-01-16 20:09:53 +03:00
bool push ( const T & data , const std : : function < bool ( ) > & test_exit )
2014-12-25 01:24:17 +03:00
{
u32 pos = 0 ;
2014-12-26 01:49:55 +03:00
while ( u32 res = m_sync . atomic_op_sync ( SQSVR_OK , [ & pos ] ( squeue_sync_var_t & sync ) - > u32
2014-12-25 01:24:17 +03:00
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
2014-12-26 01:58:43 +03:00
if ( sync . push_lock )
2014-12-25 01:24:17 +03:00
{
2014-12-26 01:49:55 +03:00
return SQSVR_LOCKED ;
}
if ( sync . count = = sq_size )
{
return SQSVR_FAILED ;
2014-12-25 01:24:17 +03:00
}
2014-12-26 01:58:43 +03:00
sync . push_lock = 1 ;
2014-12-25 01:24:17 +03:00
pos = sync . position + sync . count ;
2014-12-26 01:49:55 +03:00
return SQSVR_OK ;
2014-12-25 01:24:17 +03:00
} ) )
{
2015-01-16 20:09:53 +03:00
if ( res = = SQSVR_FAILED & & ( test_exit ( ) | | squeue_test_exit ( ) ) )
2014-12-25 01:24:17 +03: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 ) ;
2014-12-26 01:58:43 +03:00
assert ( sync . push_lock ) ;
sync . push_lock = 0 ;
2014-12-25 01:24:17 +03:00
sync . count + + ;
} ) ;
m_rcv . notify_one ( ) ;
m_wcv . notify_one ( ) ;
return true ;
}
2015-01-16 20:09:53 +03:00
bool push ( const T & data , const volatile bool * do_exit )
2014-12-25 01:24:17 +03:00
{
2015-01-16 20:09:53 +03:00
return push ( data , [ do_exit ] ( ) { return do_exit & & * do_exit ; } ) ;
}
2014-12-25 01:24:17 +03:00
2015-01-17 19:14:58 +03:00
__forceinline bool push ( const T & data )
2015-01-16 20:09:53 +03:00
{
2015-01-17 19:14:58 +03:00
return push ( data , SQUEUE_NEVER_EXIT ) ;
2014-12-25 01:24:17 +03:00
}
2015-01-17 19:14:58 +03:00
__forceinline bool try_push ( const T & data )
2015-01-16 20:09:53 +03:00
{
2015-01-17 19:14:58 +03:00
return push ( data , SQUEUE_ALWAYS_EXIT ) ;
2015-01-16 20:09:53 +03:00
}
bool pop ( T & data , const std : : function < bool ( ) > & test_exit )
2014-12-25 01:24:17 +03:00
{
u32 pos = 0 ;
2014-12-26 01:49:55 +03:00
while ( u32 res = m_sync . atomic_op_sync ( SQSVR_OK , [ & pos ] ( squeue_sync_var_t & sync ) - > u32
2014-12-25 01:24:17 +03:00
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
2014-12-26 01:49:55 +03:00
if ( ! sync . count )
{
return SQSVR_FAILED ;
2014-12-25 01:24:17 +03:00
}
2014-12-26 01:58:43 +03:00
if ( sync . pop_lock )
{
return SQSVR_LOCKED ;
}
2014-12-25 01:24:17 +03:00
2014-12-26 01:58:43 +03:00
sync . pop_lock = 1 ;
2014-12-25 01:24:17 +03:00
pos = sync . position ;
2014-12-26 01:49:55 +03:00
return SQSVR_OK ;
2014-12-25 01:24:17 +03:00
} ) )
{
2015-01-16 20:09:53 +03:00
if ( res = = SQSVR_FAILED & & ( test_exit ( ) | | squeue_test_exit ( ) ) )
2014-12-25 01:24:17 +03: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 ) ;
2014-12-26 01:58:43 +03:00
assert ( sync . pop_lock ) ;
sync . pop_lock = 0 ;
2014-12-25 01:24:17 +03:00
sync . position + + ;
sync . count - - ;
if ( sync . position = = sq_size )
{
sync . position = 0 ;
}
} ) ;
m_rcv . notify_one ( ) ;
m_wcv . notify_one ( ) ;
return true ;
}
2015-01-16 20:09:53 +03:00
bool pop ( T & data , const volatile bool * do_exit )
2014-12-25 01:24:17 +03:00
{
2015-01-16 20:09:53 +03:00
return pop ( data , [ do_exit ] ( ) { return do_exit & & * do_exit ; } ) ;
}
2014-12-25 01:24:17 +03:00
2015-01-17 19:14:58 +03:00
__forceinline bool pop ( T & data )
2015-01-16 20:09:53 +03:00
{
2015-01-17 19:14:58 +03:00
return pop ( data , SQUEUE_NEVER_EXIT ) ;
2014-12-25 01:24:17 +03:00
}
2015-01-17 19:14:58 +03:00
__forceinline bool try_pop ( T & data )
2015-01-16 20:09:53 +03:00
{
2015-01-17 19:14:58 +03:00
return pop ( data , SQUEUE_ALWAYS_EXIT ) ;
2015-01-16 20:09:53 +03:00
}
bool peek ( T & data , u32 start_pos , const std : : function < bool ( ) > & test_exit )
2014-12-25 01:24:17 +03:00
{
assert ( start_pos < sq_size ) ;
u32 pos = 0 ;
2014-12-26 01:49:55 +03:00
while ( u32 res = m_sync . atomic_op_sync ( SQSVR_OK , [ & pos , start_pos ] ( squeue_sync_var_t & sync ) - > u32
2014-12-25 01:24:17 +03:00
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
2014-12-26 01:49:55 +03:00
if ( sync . count < = start_pos )
{
return SQSVR_FAILED ;
2014-12-25 01:24:17 +03:00
}
2014-12-26 01:58:43 +03:00
if ( sync . pop_lock )
{
return SQSVR_LOCKED ;
}
2015-01-17 19:14:58 +03:00
2014-12-26 01:58:43 +03:00
sync . pop_lock = 1 ;
2014-12-25 01:24:17 +03:00
pos = sync . position + start_pos ;
2014-12-26 01:49:55 +03:00
return SQSVR_OK ;
2014-12-25 01:24:17 +03:00
} ) )
{
2015-01-16 20:09:53 +03:00
if ( res = = SQSVR_FAILED & & ( test_exit ( ) | | squeue_test_exit ( ) ) )
2014-12-25 01:24:17 +03: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 ) ;
2014-12-26 01:58:43 +03:00
assert ( sync . pop_lock ) ;
sync . pop_lock = 0 ;
2014-12-25 01:24:17 +03:00
} ) ;
m_rcv . notify_one ( ) ;
return true ;
}
2015-01-16 20:09:53 +03:00
bool peek ( T & data , u32 start_pos , const volatile bool * do_exit )
{
return peek ( data , start_pos , [ do_exit ] ( ) { return do_exit & & * do_exit ; } ) ;
}
2015-01-17 19:14:58 +03:00
__forceinline bool peek ( T & data , u32 start_pos = 0 )
2014-12-25 01:24:17 +03:00
{
2015-01-17 19:14:58 +03:00
return peek ( data , start_pos , SQUEUE_NEVER_EXIT ) ;
2015-01-16 20:09:53 +03:00
}
2014-12-25 01:24:17 +03:00
2015-01-17 19:14:58 +03:00
__forceinline bool try_peek ( T & data , u32 start_pos = 0 )
2015-01-16 20:09:53 +03:00
{
2015-01-17 19:14:58 +03:00
return peek ( data , start_pos , SQUEUE_ALWAYS_EXIT ) ;
2014-12-25 01:24:17 +03:00
}
2015-01-02 02:41:29 +03:00
class squeue_data_t
{
T * const m_data ;
const u32 m_pos ;
const u32 m_count ;
squeue_data_t ( T * data , u32 pos , u32 count )
: m_data ( data )
, m_pos ( pos )
, m_count ( count )
{
}
public :
T & operator [ ] ( u32 index )
{
assert ( index < m_count ) ;
index + = m_pos ;
index = index < sq_size ? index : index - sq_size ;
return m_data [ index ] ;
}
} ;
void process ( void ( * proc ) ( squeue_data_t data ) )
{
u32 pos , count ;
while ( m_sync . atomic_op_sync ( SQSVR_OK , [ & pos , & count ] ( squeue_sync_var_t & sync ) - > u32
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
if ( sync . pop_lock | | sync . push_lock )
{
return SQSVR_LOCKED ;
}
pos = sync . position ;
count = sync . count ;
sync . pop_lock = 1 ;
sync . push_lock = 1 ;
return SQSVR_OK ;
} ) )
{
std : : unique_lock < std : : mutex > rcv_lock ( m_rcv_mutex ) ;
m_rcv . wait_for ( rcv_lock , std : : chrono : : milliseconds ( 1 ) ) ;
}
proc ( squeue_data_t ( m_data , pos , count ) ) ;
m_sync . atomic_op ( [ ] ( squeue_sync_var_t & sync )
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
assert ( sync . pop_lock & & sync . push_lock ) ;
sync . pop_lock = 0 ;
sync . push_lock = 0 ;
} ) ;
m_wcv . notify_one ( ) ;
m_rcv . notify_one ( ) ;
}
void clear ( )
{
while ( m_sync . atomic_op_sync ( SQSVR_OK , [ ] ( squeue_sync_var_t & sync ) - > u32
{
assert ( sync . count < = sq_size ) ;
assert ( sync . position < sq_size ) ;
if ( sync . pop_lock | | sync . push_lock )
{
return SQSVR_LOCKED ;
}
sync . pop_lock = 1 ;
sync . push_lock = 1 ;
return SQSVR_OK ;
} ) )
{
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 ( ) ;
}
2014-12-25 01:24:17 +03:00
} ;