#include "../include/sys/ac_sys.h"
#if defined ( AC_BUILD_OS_WINDOWS ) || \
defined ( AC_BUILD_OS_UNDER_WINDOWS )
#include <malloc.h>
#define AC_OS_ALLOCA _alloca
#else
#include <alloca.h>
#define AC_OS_ALLOCA alloca
#endif
static void AC_CDECL
ac_prv_tls_dtor
( void* );
static int AC_APIDECL
prv_thread_alloc
( ac_thread_t**,
ac_intword_t );
static void* AC_CDECL
prv_thread_entry
( void* );
static pthread_key_t g_tls_key;
static ac_sys_thread_this_t *p_this;
int AC_APIDECL
ac_sys_thread_startup
( ac_sys_thread_this_t *_this )
{
int ret;
p_this = _this;
ret = pthread_key_create
( &g_tls_key,
ac_prv_tls_dtor );
if ( ret ) { return ac_sys_error( ret ); }
ac_cpu_stack_mpmc_init( &p_this->node_cache );
return 0;
}
int AC_APIDECL
ac_sys_thread_shutdown
( void )
{
int ret;
ac_sys_thread_tls_shutdown();
ret = pthread_key_delete( g_tls_key );
if ( ret ) { return ac_sys_error( ret ); }
if ( p_this->tls_count ) { assert( ! p_this->tls_count ); abort(); }
return 0;
}
void AC_APIDECL
ac_sys_thread_tls_shutdown
( void )
{
ac_thread_t *_this =
pthread_getspecific( g_tls_key );
if ( _this ) { ac_prv_tls_dtor( _this ); }
}
void AC_APIDECL
ac_sys_thread_cache_flush
( void )
{
ac_i686_node_t *next;
ac_thread_t *_this;
while ( p_this->node_cache.front )
{
next = p_this->node_cache.front->next;
_this = ac_cpu_node_get_state( p_this->node_cache.front );
ac_free( _this->this_mem );
--p_this->node_count;
p_this->node_cache.front = next;
}
assert( ! p_this->node_count );
}
int AC_APIDECL
ac_sys_thread_release
( ac_thread_t *_this )
{
if ( ! ac_atomic_dec_release
( &_this->shared.refs ) )
{
int ret;
ac_cpu_node_t *next;
assert( _this->recurse_index == -1 );
ret = ac_cpu_tls_free
( &_this->cpu_atomic );
if ( ret ) { assert( ! ret ); abort(); }
while ( _this->local_cache )
{
next = _this->local_cache->next;
ac_cpu_node_cache_push( _this->local_cache );
--_this->cache_count;
_this->local_cache = next;
}
if ( _this->cache_count )
{ assert( ! _this->cache_count ); abort(); }
ac_atomic_dec_release( &p_this->tls_count );
ac_cpu_stack_mpmc_push_cas
( &p_this->node_cache,
&_this->node );
ac_atomic_inc_acquire( &p_this->node_count );
}
return 0;
}
int AC_APIDECL
ac_thread_alloc
( ac_thread_t **_pthis,
ac_thread_cfg_t *cfg,
ac_fp_thread_entry_t fp_entry,
const void *state )
{
int ret;
ac_thread_t *_this;
ret = prv_thread_alloc( &_this, 2 );
if ( ret ) { ac_sys_error( ret ); abort(); }
if ( cfg ) { _this->cfg = *cfg; }
_this->fp_entry = fp_entry;
_this->state = state;
ret = pthread_create
( &_this->thread,
0,
prv_thread_entry,
_this );
if ( ret )
{ _this->shared.refs = 1;
ac_sys_thread_release( _this );
return ac_sys_error( ret );
}
*_pthis = _this;
return 0;
}
int AC_APIDECL
ac_thread_join
( ac_thread_t *_this,
void **state )
{
int ret = pthread_join( _this->thread, state );
if ( ret ) { return ac_sys_error( ret ); }
ret = ac_sys_thread_release( _this );
if ( ret ) { return ac_sys_error( ret ); }
return 0;
}
int AC_APIDECL
prv_thread_alloc
( ac_thread_t **_pthis,
ac_intword_t refs )
{
int ret;
ac_thread_t *_this;
ac_cpu_node_t *node;
node = np_ac_cpu_stack_mpmc_pop_dwcas
( &p_this->node_cache );
if ( ! node )
{
void *this_mem;
_this = ac_calloc_aligned
( &this_mem,
1,
sizeof( *_this ),
AC_CPU_CACHE_LINE );
if ( ! _this ) { return ac_sys_error( ENOMEM ); }
_this->this_mem = this_mem;
ac_cpu_node_init( &_this->node, 0, _this );
_this->id = ac_atomic_inc_acquire( (ac_intword_t*)&p_this->thread_ids );
assert( _this->id );
}
else
{
ac_atomic_dec_release( &p_this->node_count );
_this = ac_cpu_node_get_state( node );
_this->cache_count = 0;
_this->local_cache = 0;
}
_this->shared.refs = refs;
_this->recurse_index = -1;
ret = ac_cpu_tls_alloc
( &_this->cpu_atomic,
_this );
if ( ret ) { return ac_sys_error( ret ); }
ac_atomic_inc_acquire( &p_this->tls_count );
*_pthis = _this;
return 0;
}
ac_thread_t* AC_APIDECL
ac_thread_self
( void )
{
ac_thread_t *_this =
pthread_getspecific( g_tls_key );
if ( ! _this )
{
int ret = prv_thread_alloc( &_this, 1 );
if ( ret ) { ac_sys_error( ret ); abort(); }
_this->thread = pthread_self();
ret = pthread_setspecific
( g_tls_key,
_this );
if ( ret )
{ ac_sys_thread_release( _this );
ac_sys_error( ret );
abort();
}
}
return _this;
}
void AC_CDECL
ac_prv_tls_dtor
( void *ts )
{
if ( ts )
{
int ret;
ret = ac_sys_thread_release( ts );
if ( ret ) { assert( ! ret ); abort(); }
ret = pthread_setspecific
( g_tls_key,
0 );
if ( ret ) { assert( ! ret ); abort(); }
}
}
void* AC_CDECL
prv_thread_entry
( void *state )
{
int ret;
void *uret;
ac_thread_t *_this = state;
ret = pthread_setspecific
( g_tls_key,
_this );
if ( ret ) { assert( ! ret ); abort(); }
if ( _this->id < 64 )
{
AC_OS_ALLOCA( 2048 * _this->id );
uret = _this->fp_entry( (void*)_this->state );
}
else
{
uret = _this->fp_entry( (void*)_this->state );
}
return uret;
}