#include "../../../include/sys/ac_sys.h"
#include "../../../include/ac_rwspinlock_algo1.h"
#include "../../../include/ac_dlist.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
#ifdef _MSC_VER
#pragma warning ( disable : 4324 )
#pragma pack(1)
#endif
typedef struct
AC_DECLSPEC_PACKED
ac_prv_smr_dlist_
{
ac_i686_lfgc_smr_t *front;
ac_i686_lfgc_smr_t *back;
} ac_prv_smr_dlist_t;
typedef struct
AC_DECLSPEC_PACKED_ALIGN_CACHE_LINE
ac_prv_smr_shared_
{
ac_rwspinlock_algo1_shared_padded_t mutex_impl;
ac_prv_smr_dlist_t active;
ac_prv_smr_dlist_t cache;
ac_i686_intword_t active_count;
} ac_prv_smr_shared_t;
#ifdef _MSC_VER
#pragma pack()
#pragma warning ( default : 4324 )
#endif
typedef struct ac_prv_smr_
{
ac_prv_smr_shared_t shared;
ac_rwspinlock_algo1_t mutex_impl;
void *this_mem;
} ac_prv_smr_t;
static void AC_APIDECL
ac_prv_smr_hazard_scan
( ac_i686_lfgc_smr_t*,
ac_i686_node_t**,
ac_i686_intword_t );
static ac_prv_smr_t *g_smr;
int AC_APIDECL
ac_i686_lfgc_smr_startup
( void )
{
int ret;
void *this_mem;
g_smr = ac_calloc_aligned
( &this_mem,
1,
sizeof( *g_smr ),
AC_CPU_CACHE_LINE );
if ( ! g_smr ) { return ac_sys_error( ENOMEM ); }
g_smr->this_mem = this_mem;
ret = ac_rwspinlock_algo1_alloc
( &g_smr->mutex_impl,
&g_smr->shared.mutex_impl._this );
if ( ret ) { ac_free( this_mem ); return ac_sys_error( ret ); }
return 0;
}
int AC_APIDECL
ac_i686_lfgc_smr_shutdown
( void )
{
ac_i686_lfgc_smr_t *next;
ac_i686_node_t *node_next;
while ( g_smr->shared.cache.front )
{
next = g_smr->shared.cache.front->next;
while ( g_smr->shared.cache.front->cache_front )
{
node_next = g_smr->shared.cache.front->cache_front->lfgc_next;
if ( ac_thread_cpu_node_cache_push_no_free
( 0,
g_smr->shared.cache.front->cache_front ) )
{
ac_free( g_smr->shared.cache.front->cache_front );
}
--g_smr->shared.cache.front->cache_count;
g_smr->shared.cache.front->cache_front = node_next;
}
assert( ! g_smr->shared.cache.front->cache_count );
ac_free( g_smr->shared.cache.front->this_mem );
g_smr->shared.cache.front = next;
}
ac_rwspinlock_algo1_free( &g_smr->mutex_impl );
ac_free( g_smr->this_mem );
return 0;
}
int AC_APIDECL
ac_i686_lfgc_smr_alloc
( ac_i686_tls_t *_this )
{
int ret;
ac_i686_lfgc_smr_t *smr;
ret = ac_rwspinlock_algo1_write_lock
( &g_smr->mutex_impl,
_this->thread );
if ( ret ) { ac_sys_error( ret ); abort(); }
smr = ac_dlist_pop_front_cast
( &g_smr->shared.cache );
assert( ! _this->lfgc_smr );
if ( ! smr )
{
void *this_mem;
smr = ac_calloc_aligned
( &this_mem,
1,
sizeof( *smr ),
AC_CPU_CACHE_LINE );
if ( ! smr )
{ ac_rwspinlock_algo1_write_unlock
( &g_smr->mutex_impl,
_this->thread );
return ac_sys_error( ENOMEM );
}
smr->this_mem = this_mem;
}
_this->lfgc_smr = smr;
smr->_tls = _this;
ac_dlist_push_back_cast
( &g_smr->shared.active,
smr );
++g_smr->shared.active_count;
ret = ac_rwspinlock_algo1_write_unlock
( &g_smr->mutex_impl,
_this->thread );
if ( ret ) { ac_sys_error( ret ); abort(); }
return 0;
}
int AC_APIDECL
ac_i686_lfgc_smr_free
( ac_i686_tls_t *_this )
{
if ( _this->lfgc_smr )
{
int ret;
ac_i686_lfgc_smr_t *smr = _this->lfgc_smr;
ac_i686_lfgc_smr_scan( smr );
smr->_tls = 0;
_this->lfgc_smr = 0;
ret = ac_rwspinlock_algo1_write_lock
( &g_smr->mutex_impl,
_this->thread );
if ( ret ) { ac_sys_error( ret ); abort(); }
ac_dlist_pop_cast
( &g_smr->shared.active,
smr );
ac_dlist_push_back_cast
( &g_smr->shared.cache,
smr );
--g_smr->shared.active_count;
ret = ac_rwspinlock_algo1_write_unlock
( &g_smr->mutex_impl,
_this->thread );
if ( ret ) { ac_sys_error( ret ); abort(); }
}
return 0;
}
void AC_APIDECL
ac_prv_smr_hazard_scan
( ac_i686_lfgc_smr_t *_this,
ac_i686_node_t **hazards,
ac_i686_intword_t haz_depth )
{
int i;
ac_i686_node_t *cur, *next, *prev;
cur = _this->cache_front;
prev = cur;
while ( cur )
{
next = cur->lfgc_next;
for ( i = 0; i < haz_depth; ++i )
{
if ( cur == hazards[i] ) { break; }
}
if ( i == haz_depth )
{
--_this->cache_count;
if ( cur->fp_dtor &&
cur->fp_dtor != (ac_fp_dtor_t)0x00000001 )
{
cur->fp_dtor( cur );
}
else
{
ac_thread_cpu_node_cache_push
( _this->_tls->thread,
cur );
}
if ( prev == cur )
{
_this->cache_front = next;
cur = next;
prev = cur;
}
else
{
prev->lfgc_next = next;
cur = next;
}
}
else
{
prev = cur;
cur = next;
}
}
if ( ! haz_depth ) { _this->cache_front = 0; }
if ( ! _this->cache_front && _this->cache_count )
{
ac_sys_error( AC_ECORRUPTED );
abort();
}
}
void AC_APIDECL
ac_i686_lfgc_smr_scan
( ac_i686_lfgc_smr_t *_this )
{
int ret, i = 0, x;
ac_i686_intword_t smr_depth;
ac_i686_node_t **hazards = 0, *temp;
ac_i686_lfgc_smr_t *smr_front;
ret = ac_rwspinlock_algo1_read_lock
( &g_smr->mutex_impl,
_this->_tls->thread );
if ( ret ) { ac_sys_error( ret ); abort(); }
smr_depth = g_smr->shared.active_count * AC_I686_LFGC_SMR_HAZARD_DEPTH;
smr_front = g_smr->shared.active.front;
hazards = AC_OS_ALLOCA( sizeof( smr_front ) * smr_depth );
while ( smr_front )
{
if ( smr_front != _this )
{
for ( x = 0; x < AC_I686_LFGC_SMR_HAZARD_DEPTH; ++x )
{
temp = ac_mb_loadptr_depends( &smr_front->hazards[x] );
if ( temp )
{
hazards[i++] = temp;
}
}
}
smr_front = smr_front->next;
}
ret = ac_rwspinlock_algo1_read_unlock
( &g_smr->mutex_impl,
_this->_tls->thread );
if ( ret ) { ac_sys_error( ret ); abort(); }
ac_prv_smr_hazard_scan
( _this,
hazards,
i );
}