1999-04-23 19:00:02 +08:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Thread Safe Resource Manager |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2000-09-01 03:02:14 +08:00
| Copyright ( c ) 1999 , 2000 , Andi Gutmans , Sascha Schumann , Zeev Suraski |
| This source file is subject to the TSRM license , that is bundled |
| with this package in the file LICENSE |
1999-04-23 19:00:02 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2000-09-01 03:02:14 +08:00
| Authors : Zeev Suraski < zeev @ zend . com > |
1999-04-23 19:00:02 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
1999-04-21 07:58:02 +08:00
# include "TSRM.h"
2000-09-01 07:44:10 +08:00
# ifdef ZTS
1999-04-21 07:58:02 +08:00
# include <stdio.h>
1999-05-07 05:13:26 +08:00
# include <stdlib.h>
1999-04-21 07:58:02 +08:00
1999-04-24 17:01:30 +08:00
# if HAVE_STDARG_H
# include <stdarg.h>
# endif
1999-04-21 07:58:02 +08:00
typedef struct _tsrm_tls_entry tsrm_tls_entry ;
struct _tsrm_tls_entry {
void * * storage ;
int count ;
THREAD_T thread_id ;
tsrm_tls_entry * next ;
} ;
typedef struct {
size_t size ;
1999-12-06 00:21:37 +08:00
ts_allocate_ctor ctor ;
ts_allocate_dtor dtor ;
1999-04-21 07:58:02 +08:00
} tsrm_resource_type ;
2000-11-18 07:31:15 +08:00
# define TSRM_SHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)+1)
# define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)-1)
1999-04-21 07:58:02 +08:00
/* The memory manager table */
2000-11-18 07:31:15 +08:00
static tsrm_tls_entry * * tsrm_tls_table = NULL ;
1999-04-21 07:58:02 +08:00
static int tsrm_tls_table_size ;
static ts_rsrc_id id_count ;
/* The resource sizes table */
2000-11-18 07:31:15 +08:00
static tsrm_resource_type * resource_types_table = NULL ;
1999-04-21 07:58:02 +08:00
static int resource_types_table_size ;
static MUTEX_T tsmm_mutex ; /* thread-safe memory manager mutex */
1999-08-14 17:35:52 +08:00
/* New thread handlers */
static void ( * tsrm_new_thread_begin_handler ) ( ) ;
static void ( * tsrm_new_thread_end_handler ) ( ) ;
1999-04-21 07:58:02 +08:00
/* Debug support */
2000-11-18 10:41:14 +08:00
int tsrm_error ( int level , const char * format , . . . ) ;
2000-11-18 07:31:15 +08:00
static int tsrm_error_level ;
2000-11-18 10:41:14 +08:00
static FILE * tsrm_error_file ;
# if TSRM_DEBUG
2000-11-18 07:31:15 +08:00
# define TSRM_ERROR tsrm_error
# define TSRM_SAFE_ARRAY_OFFSET(array, offset, range) (((offset)>=0 && (offset)<(range)) ? array[offset] : NULL)
# else
# define TSRM_ERROR
# define TSRM_SAFE_ARRAY_OFFSET(array, offset, range) array[offset]
# endif
1999-04-21 07:58:02 +08:00
/* Startup TSRM (call once for the entire process) */
2000-11-18 10:41:14 +08:00
TSRM_API int tsrm_startup ( int expected_threads , int expected_resources , int debug_level , char * debug_filename )
1999-04-21 07:58:02 +08:00
{
1999-12-06 05:55:43 +08:00
# if defined(GNUPTH)
pth_init ( ) ;
# endif
2000-11-18 10:41:14 +08:00
tsrm_error_file = stderr ;
tsrm_error_set ( debug_level , debug_filename ) ;
1999-04-21 07:58:02 +08:00
tsrm_tls_table_size = expected_threads ;
2000-11-18 10:41:14 +08:00
1999-04-21 07:58:02 +08:00
tsrm_tls_table = ( tsrm_tls_entry * * ) calloc ( tsrm_tls_table_size , sizeof ( tsrm_tls_entry * ) ) ;
if ( ! tsrm_tls_table ) {
2000-11-18 07:31:15 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_ERROR , " Unable to allocate TLS table " ) ;
1999-04-21 07:58:02 +08:00
return 0 ;
}
id_count = 0 ;
resource_types_table_size = expected_resources ;
resource_types_table = ( tsrm_resource_type * ) calloc ( resource_types_table_size , sizeof ( tsrm_resource_type ) ) ;
if ( ! resource_types_table ) {
2000-11-18 07:31:15 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_ERROR , " Unable to allocate resource types table " ) ;
1999-04-21 07:58:02 +08:00
free ( tsrm_tls_table ) ;
2000-11-18 07:31:15 +08:00
tsrm_tls_table = NULL ;
1999-04-21 07:58:02 +08:00
return 0 ;
}
tsmm_mutex = tsrm_mutex_alloc ( ) ;
1999-08-14 17:35:52 +08:00
tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL ;
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_CORE , " Started up TSRM, %d expected threads, %d expected resources " , expected_threads , expected_resources ) ;
1999-04-21 07:58:02 +08:00
return 1 ;
}
/* Shutdown TSRM (call once for the entire process) */
1999-12-06 00:21:37 +08:00
TSRM_API void tsrm_shutdown ( void )
1999-04-21 07:58:02 +08:00
{
int i ;
if ( tsrm_tls_table ) {
for ( i = 0 ; i < tsrm_tls_table_size ; i + + ) {
tsrm_tls_entry * p = tsrm_tls_table [ i ] , * next_p ;
while ( p ) {
int j ;
next_p = p - > next ;
for ( j = 0 ; j < id_count ; j + + ) {
free ( p - > storage [ j ] ) ;
}
free ( p - > storage ) ;
free ( p ) ;
p = next_p ;
}
}
free ( tsrm_tls_table ) ;
2000-11-18 07:31:15 +08:00
tsrm_tls_table = NULL ;
1999-04-21 07:58:02 +08:00
}
if ( resource_types_table ) {
free ( resource_types_table ) ;
2000-11-18 07:31:15 +08:00
resource_types_table = NULL ;
1999-04-21 07:58:02 +08:00
}
tsrm_mutex_free ( tsmm_mutex ) ;
2000-11-18 07:31:15 +08:00
tsmm_mutex = NULL ;
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_CORE , " Shutdown TSRM " ) ;
if ( tsrm_error_file ! = stderr ) {
fclose ( tsrm_error_file ) ;
}
1999-12-06 05:55:43 +08:00
# if defined(GNUPTH)
pth_kill ( ) ;
# endif
1999-04-21 07:58:02 +08:00
}
/* allocates a new thread-safe-resource id */
1999-12-06 00:21:37 +08:00
TSRM_API ts_rsrc_id ts_allocate_id ( size_t size , ts_allocate_ctor ctor , ts_allocate_dtor dtor )
1999-04-21 07:58:02 +08:00
{
ts_rsrc_id new_id ;
int i ;
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_CORE , " Obtaining a new resource id, %d bytes " , size ) ;
1999-04-21 07:58:02 +08:00
tsrm_mutex_lock ( tsmm_mutex ) ;
/* obtain a resource id */
new_id = id_count + + ;
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_CORE , " Obtained resource id %d " , TSRM_SHUFFLE_RSRC_ID ( new_id ) ) ;
1999-04-21 07:58:02 +08:00
/* store the new resource type in the resource sizes table */
if ( resource_types_table_size < id_count ) {
resource_types_table = ( tsrm_resource_type * ) realloc ( resource_types_table , sizeof ( tsrm_resource_type ) * id_count ) ;
if ( ! resource_types_table ) {
2000-11-18 07:31:15 +08:00
tsrm_mutex_unlock ( tsmm_mutex ) ;
TSRM_ERROR ( TSRM_ERROR_LEVEL_ERROR , " Unable to allocate storage for resource " ) ;
return 0 ;
1999-04-21 07:58:02 +08:00
}
resource_types_table_size = id_count ;
}
resource_types_table [ new_id ] . size = size ;
resource_types_table [ new_id ] . ctor = ctor ;
resource_types_table [ new_id ] . dtor = dtor ;
/* enlarge the arrays for the already active threads */
for ( i = 0 ; i < tsrm_tls_table_size ; i + + ) {
tsrm_tls_entry * p = tsrm_tls_table [ i ] ;
while ( p ) {
if ( p - > count < id_count ) {
int j ;
1999-04-24 17:01:30 +08:00
p - > storage = ( void * ) realloc ( p - > storage , sizeof ( void * ) * id_count ) ;
1999-04-21 07:58:02 +08:00
for ( j = p - > count ; j < id_count ; j + + ) {
p - > storage [ j ] = ( void * ) malloc ( resource_types_table [ j ] . size ) ;
1999-04-23 19:00:02 +08:00
if ( resource_types_table [ j ] . ctor ) {
resource_types_table [ j ] . ctor ( p - > storage [ j ] ) ;
}
1999-04-21 07:58:02 +08:00
}
p - > count = id_count ;
}
p = p - > next ;
}
}
tsrm_mutex_unlock ( tsmm_mutex ) ;
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_CORE , " Successfully allocated new resource id %d " , TSRM_SHUFFLE_RSRC_ID ( new_id ) ) ;
2000-11-18 07:31:15 +08:00
return TSRM_SHUFFLE_RSRC_ID ( new_id ) ;
1999-04-21 07:58:02 +08:00
}
static void allocate_new_resource ( tsrm_tls_entry * * thread_resources_ptr , THREAD_T thread_id )
{
int i ;
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_CORE , " Creating data structures for thread %x " , thread_id ) ;
1999-04-21 07:58:02 +08:00
( * thread_resources_ptr ) = ( tsrm_tls_entry * ) malloc ( sizeof ( tsrm_tls_entry ) ) ;
( * thread_resources_ptr ) - > storage = ( void * * ) malloc ( sizeof ( void * ) * id_count ) ;
( * thread_resources_ptr ) - > count = id_count ;
( * thread_resources_ptr ) - > thread_id = thread_id ;
( * thread_resources_ptr ) - > next = NULL ;
1999-10-10 21:30:03 +08:00
tsrm_mutex_unlock ( tsmm_mutex ) ;
if ( tsrm_new_thread_begin_handler ) {
tsrm_new_thread_begin_handler ( thread_id ) ;
}
1999-04-21 07:58:02 +08:00
for ( i = 0 ; i < id_count ; i + + ) {
( * thread_resources_ptr ) - > storage [ i ] = ( void * ) malloc ( resource_types_table [ i ] . size ) ;
1999-04-23 19:00:02 +08:00
if ( resource_types_table [ i ] . ctor ) {
resource_types_table [ i ] . ctor ( ( * thread_resources_ptr ) - > storage [ i ] ) ;
}
1999-04-21 07:58:02 +08:00
}
1999-08-14 17:35:52 +08:00
if ( tsrm_new_thread_end_handler ) {
tsrm_new_thread_end_handler ( thread_id ) ;
}
1999-04-21 07:58:02 +08:00
}
/* fetches the requested resource for the current thread */
2000-06-17 22:56:19 +08:00
TSRM_API void * ts_resource_ex ( ts_rsrc_id id , THREAD_T * th_id )
1999-04-21 07:58:02 +08:00
{
2000-06-16 22:38:05 +08:00
THREAD_T thread_id ;
1999-04-21 07:58:02 +08:00
int hash_value ;
tsrm_tls_entry * thread_resources ;
void * resource ;
2000-06-16 22:38:05 +08:00
if ( th_id ) {
thread_id = * th_id ;
} else {
thread_id = tsrm_thread_id ( ) ;
}
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_INFO , " Fetching resource id %d for thread %ld " , id , ( long ) thread_id ) ;
1999-04-21 07:58:02 +08:00
tsrm_mutex_lock ( tsmm_mutex ) ;
hash_value = THREAD_HASH_OF ( thread_id , tsrm_tls_table_size ) ;
thread_resources = tsrm_tls_table [ hash_value ] ;
if ( ! thread_resources ) {
allocate_new_resource ( & tsrm_tls_table [ hash_value ] , thread_id ) ;
1999-10-10 21:30:03 +08:00
return ts_resource ( id ) ;
/* thread_resources = tsrm_tls_table[hash_value]; */
1999-04-21 07:58:02 +08:00
} else {
do {
if ( thread_resources - > thread_id = = thread_id ) {
break ;
}
if ( thread_resources - > next ) {
thread_resources = thread_resources - > next ;
} else {
allocate_new_resource ( & thread_resources - > next , thread_id ) ;
1999-10-10 21:30:03 +08:00
return ts_resource ( id ) ;
/*
* thread_resources = thread_resources - > next ;
* break ;
*/
1999-04-21 07:58:02 +08:00
}
} while ( thread_resources ) ;
}
2000-11-18 07:31:15 +08:00
resource = TSRM_SAFE_ARRAY_OFFSET ( thread_resources - > storage , TSRM_UNSHUFFLE_RSRC_ID ( id ) , thread_resources - > count ) ;
1999-04-21 07:58:02 +08:00
tsrm_mutex_unlock ( tsmm_mutex ) ;
2000-11-18 07:31:15 +08:00
if ( resource ) {
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_INFO , " Successfully fetched resource id %d for thread id %ld - %x " , id , ( long ) thread_id , ( long ) resource ) ;
2000-11-18 07:31:15 +08:00
} else {
TSRM_ERROR ( TSRM_ERROR_LEVEL_ERROR , " Resource id %d is out of range (%d..%d) " , id , TSRM_SHUFFLE_RSRC_ID ( 0 ) , TSRM_SHUFFLE_RSRC_ID ( thread_resources - > count - 1 ) ) ;
}
1999-04-21 07:58:02 +08:00
return resource ;
}
/* frees all resources allocated for the current thread */
1999-12-06 00:21:37 +08:00
void ts_free_thread ( void )
1999-04-21 07:58:02 +08:00
{
THREAD_T thread_id = tsrm_thread_id ( ) ;
int hash_value ;
tsrm_tls_entry * thread_resources ;
tsrm_tls_entry * last = NULL ;
tsrm_mutex_lock ( tsmm_mutex ) ;
hash_value = THREAD_HASH_OF ( thread_id , tsrm_tls_table_size ) ;
thread_resources = tsrm_tls_table [ hash_value ] ;
while ( thread_resources ) {
if ( thread_resources - > thread_id = = thread_id ) {
int i ;
for ( i = 0 ; i < thread_resources - > count ; i + + ) {
1999-04-23 19:00:02 +08:00
if ( resource_types_table [ i ] . dtor ) {
resource_types_table [ i ] . dtor ( thread_resources - > storage [ i ] ) ;
}
1999-04-21 07:58:02 +08:00
free ( thread_resources - > storage [ i ] ) ;
}
free ( thread_resources - > storage ) ;
if ( last ) {
last - > next = thread_resources - > next ;
} else {
tsrm_tls_table [ hash_value ] = NULL ;
}
free ( thread_resources ) ;
break ;
}
if ( thread_resources - > next ) {
last = thread_resources ;
}
1999-05-12 05:27:44 +08:00
thread_resources = thread_resources - > next ;
1999-04-21 07:58:02 +08:00
}
tsrm_mutex_unlock ( tsmm_mutex ) ;
}
/* deallocates all occurrences of a given id */
void ts_free_id ( ts_rsrc_id id )
{
}
/*
* Utility Functions
*/
/* Obtain the current thread id */
1999-08-14 17:35:52 +08:00
TSRM_API THREAD_T tsrm_thread_id ( void )
1999-04-21 07:58:02 +08:00
{
2000-09-02 23:03:19 +08:00
# ifdef TSRM_WIN32
1999-04-21 07:58:02 +08:00
return GetCurrentThreadId ( ) ;
1999-12-06 05:55:43 +08:00
# elif defined(GNUPTH)
return pth_self ( ) ;
1999-04-21 07:58:02 +08:00
# elif defined(PTHREADS)
return pthread_self ( ) ;
# elif defined(NSAPI)
return systhread_current ( ) ;
# elif defined(PI3WEB)
return PIThread_getCurrent ( ) ;
# endif
}
/* Allocate a mutex */
2000-11-18 07:31:15 +08:00
TSRM_API MUTEX_T tsrm_mutex_alloc ( void )
1999-04-21 07:58:02 +08:00
{
MUTEX_T mutexp ;
2000-09-02 23:03:19 +08:00
# ifdef TSRM_WIN32
1999-12-21 04:02:25 +08:00
mutexp = malloc ( sizeof ( CRITICAL_SECTION ) ) ;
InitializeCriticalSection ( mutexp ) ;
1999-12-06 05:55:43 +08:00
# elif defined(GNUPTH)
mutexp = ( MUTEX_T ) malloc ( sizeof ( * mutexp ) ) ;
pth_mutex_init ( mutexp ) ;
1999-04-21 07:58:02 +08:00
# elif defined(PTHREADS)
mutexp = ( pthread_mutex_t * ) malloc ( sizeof ( pthread_mutex_t ) ) ;
pthread_mutex_init ( mutexp , NULL ) ;
# elif defined(NSAPI)
mutexp = crit_init ( ) ;
# elif defined(PI3WEB)
mutexp = PIPlatform_allocLocalMutex ( ) ;
# endif
# ifdef THR_DEBUG
printf ( " Mutex created thread: %d \n " , mythreadid ( ) ) ;
# endif
return ( mutexp ) ;
}
/* Free a mutex */
2000-11-18 07:31:15 +08:00
TSRM_API void tsrm_mutex_free ( MUTEX_T mutexp )
1999-04-21 07:58:02 +08:00
{
if ( mutexp ) {
2000-09-02 23:03:19 +08:00
# ifdef TSRM_WIN32
1999-12-21 04:02:25 +08:00
DeleteCriticalSection ( mutexp ) ;
1999-12-06 05:55:43 +08:00
# elif defined(GNUPTH)
free ( mutexp ) ;
1999-04-21 07:58:02 +08:00
# elif defined(PTHREADS)
1999-11-26 23:33:42 +08:00
pthread_mutex_destroy ( mutexp ) ;
1999-12-06 05:55:43 +08:00
free ( mutexp ) ;
1999-04-21 07:58:02 +08:00
# elif defined(NSAPI)
crit_terminate ( mutexp ) ;
# elif defined(PI3WEB)
PISync_delete ( mutexp )
# endif
}
# ifdef THR_DEBUG
printf ( " Mutex freed thread: %d \n " , mythreadid ( ) ) ;
# endif
}
/* Lock a mutex */
2000-11-18 07:31:15 +08:00
TSRM_API int tsrm_mutex_lock ( MUTEX_T mutexp )
1999-04-21 07:58:02 +08:00
{
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_INFO , " Mutex locked thread: %ld " , tsrm_thread_id ( ) ) ;
2000-09-02 23:03:19 +08:00
# ifdef TSRM_WIN32
1999-12-21 04:02:25 +08:00
EnterCriticalSection ( mutexp ) ;
return 1 ;
1999-12-06 05:55:43 +08:00
# elif defined(GNUPTH)
return pth_mutex_acquire ( mutexp , 0 , NULL ) ;
1999-04-21 07:58:02 +08:00
# elif defined(PTHREADS)
return pthread_mutex_lock ( mutexp ) ;
# elif defined(NSAPI)
return crit_enter ( mutexp ) ;
# elif defined(PI3WEB)
return PISync_lock ( mutexp ) ;
# endif
}
/* Unlock a mutex */
2000-11-18 07:31:15 +08:00
TSRM_API int tsrm_mutex_unlock ( MUTEX_T mutexp )
1999-04-21 07:58:02 +08:00
{
2000-11-18 10:41:14 +08:00
TSRM_ERROR ( TSRM_ERROR_LEVEL_INFO , " Mutex unlocked thread: %ld " , tsrm_thread_id ( ) ) ;
2000-09-02 23:03:19 +08:00
# ifdef TSRM_WIN32
1999-12-21 04:02:25 +08:00
LeaveCriticalSection ( mutexp ) ;
return 1 ;
1999-12-06 05:55:43 +08:00
# elif defined(GNUPTH)
return pth_mutex_release ( mutexp ) ;
1999-04-21 07:58:02 +08:00
# elif defined(PTHREADS)
return pthread_mutex_unlock ( mutexp ) ;
# elif defined(NSAPI)
return crit_exit ( mutexp ) ;
# elif defined(PI3WEB)
return PISync_unlock ( mutexp ) ;
# endif
}
1999-08-14 17:35:52 +08:00
TSRM_API void * tsrm_set_new_thread_begin_handler ( void ( * new_thread_begin_handler ) ( THREAD_T thread_id ) )
{
void * retval = ( void * ) tsrm_new_thread_begin_handler ;
tsrm_new_thread_begin_handler = new_thread_begin_handler ;
return retval ;
}
TSRM_API void * tsrm_set_new_thread_end_handler ( void ( * new_thread_end_handler ) ( THREAD_T thread_id ) )
{
void * retval = ( void * ) tsrm_new_thread_end_handler ;
tsrm_new_thread_end_handler = new_thread_end_handler ;
return retval ;
}
1999-04-21 07:58:02 +08:00
/*
* Debug support
*/
2000-11-18 10:41:14 +08:00
# if TSRM_DEBUG
int tsrm_error ( int level , const char * format , . . . )
1999-04-21 07:58:02 +08:00
{
2000-11-18 07:31:15 +08:00
if ( level < = tsrm_error_level ) {
1999-04-21 07:58:02 +08:00
va_list args ;
int size ;
2000-11-18 10:41:14 +08:00
fprintf ( tsrm_error_file , " TSRM: " ) ;
1999-04-21 07:58:02 +08:00
va_start ( args , format ) ;
2000-11-18 10:41:14 +08:00
size = vfprintf ( tsrm_error_file , format , args ) ;
1999-04-21 07:58:02 +08:00
va_end ( args ) ;
2000-11-18 10:41:14 +08:00
fprintf ( tsrm_error_file , " \n " ) ;
fflush ( tsrm_error_file ) ;
1999-04-21 07:58:02 +08:00
return size ;
} else {
return 0 ;
}
}
2000-11-18 10:41:14 +08:00
# endif
1999-04-21 07:58:02 +08:00
2000-11-18 10:41:14 +08:00
void tsrm_error_set ( int level , char * debug_filename )
1999-04-21 07:58:02 +08:00
{
2000-11-18 07:31:15 +08:00
tsrm_error_level = level ;
2000-11-18 10:41:14 +08:00
# if TSRM_DEBUG
if ( tsrm_error_file ! = stderr ) { /* close files opened earlier */
fclose ( tsrm_error_file ) ;
}
if ( debug_filename ) {
tsrm_error_file = fopen ( debug_filename , " w " ) ;
if ( ! tsrm_error_file ) {
tsrm_error_file = stderr ;
}
} else {
tsrm_error_file = stderr ;
}
# endif
1999-04-24 17:01:30 +08:00
}
2000-09-01 07:44:10 +08:00
# endif /* ZTS */