diff options
-rw-r--r-- | ChangeLog | 50 | ||||
-rw-r--r-- | attr.c | 41 | ||||
-rw-r--r-- | cleanup.c | 100 | ||||
-rw-r--r-- | create.c | 171 | ||||
-rw-r--r-- | exit.c | 33 | ||||
-rw-r--r-- | implement.h | 48 | ||||
-rw-r--r-- | private.c | 158 | ||||
-rw-r--r-- | pthread.h | 115 |
8 files changed, 498 insertions, 218 deletions
@@ -1,3 +1,53 @@ +Wed Jul 22 00:16:22 1998 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * attr.c, implement.h, pthread.h, ChangeLog: Resolve CVS merge + conflicts. + + * private.c (_pthread_find_thread_entry): Changes to return type + to support leaner _pthread_threads_table[] which now only stores + _pthread_thread_thread_t *. + (_pthread_new_thread_entry): Internal changes. + (_pthread_delete_thread_entry): Internal changes to avoid contention. + Calling routines changed accordingly. + + * pthread.h: Modified cleanup macros to use new generic push and pop. + Added destructor and atfork stacks to _pthread_threads_thread_t. + + * cleanup.c (_pthread_handler_push, _pthread_handler_pop, + _pthread_handler_pop_all): Renamed cleanup push and pop routines + and made generic to handle destructors and atfork handlers as + well. + + * create.c (_pthread_start_call): New function is a wrapper for + all new threads. It allows us to do some cleanup when the thread + returns, ie. that is otherwise only done if the thread is cancelled. + + * exit.c (_pthread_vacuum): New function contains code from + pthread_exit() that we need in the new _pthread_start_call() + as well. + + * implement.h: Various additions and minor changes. + + * pthread.h: Various additions and minor changes. + Change cleanup handler macros to use generic handler push and pop + functions. + + * attr.c: Minor mods to all functions. + (is_attr): Implemented missing function. + + * create.c (pthread_create): More clean up. + + * private.c (_pthread_find_thread_entry): Implement. + (_pthread_delete_thread_entry): Implement. + (_pthread_new_thread_entry): Implement. + These functions manipulate the implementations internal thread + table and are part of general code cleanup and modularisation. + They replace _pthread_getthreadindex() which was removed. + + * exit.c (pthread_exit): Changed to use the new code above. + + * pthread.h: Add cancelability constants. Update comments. + 1998-07-22 Ben Elliston <bje@cygnus.com> * attr.c (pthread_setstacksize): Update test of attr argument. @@ -8,6 +8,16 @@ #include "pthread.h" #include "implement.h" +static int +is_attr(pthread_attr_t *attr) +{ + /* Return 0 if the attr object is valid, 1 otherwise. */ + + return (attr == NULL || attr->valid != _PTHREAD_ATTR_VALID); +} + +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) @@ -18,7 +28,7 @@ pthread_attr_setstacksize(pthread_attr_t *attr, return EINVAL; } - if (attr == NULL) + if (is_attr(attr) != 0) { return EINVAL; } @@ -32,7 +42,7 @@ int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) { - if (attr == NULL) + if (is_attr(attr) != 0) { return EINVAL; } @@ -42,11 +52,15 @@ pthread_attr_getstacksize(const pthread_attr_t *attr, return 0; } +#endif /* _POSIX_THREAD_ATTR_STACKSIZE */ + +#ifdef _POSIX_THREAD_ATTR_STACKADDR + int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) { - if (attr == NULL) + if (is_attr(attr) != 0) { return EINVAL; } @@ -59,7 +73,7 @@ int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) { - if (attr == NULL) + if (is_attr(attr) != 0) { return EINVAL; } @@ -68,6 +82,9 @@ pthread_attr_getstackaddr(const pthread_attr_t *attr, return ENOSYS; } +#endif /* _POSIX_THREAD_ATTR_STACKADDR */ + + int pthread_attr_init(pthread_attr_t *attr) { @@ -77,8 +94,12 @@ pthread_attr_init(pthread_attr_t *attr) return EINVAL; } - /* FIXME: Fill out the structure with default values. */ - attr->stacksize = 0; +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + attr->stacksize = PTHREAD_STACK_MIN; +#endif + + attr->cancelability = _PTHREAD_CANCEL_DEFAULTS; + attr->valid = 0; return 0; } @@ -86,11 +107,13 @@ pthread_attr_init(pthread_attr_t *attr) int pthread_attr_destroy(pthread_attr_t *attr) { - if (attr == NULL) + if (is_attr(attr) != 0) { return EINVAL; } - - /* Nothing to do. */ + + /* Set the attribute object to a specific invalid value. */ + attr->valid = _PTHREAD_ATTR_INVALID; + return 0; } @@ -10,58 +10,82 @@ #include "implement.h" void -_pthread_cleanup_push(void (*routine)(void *), void *arg) +_pthread_handler_push(_pthread_handler_node_t ** stacktop, + int poporder, + void (*routine)(void *), + void *arg) { - _pthread_cleanup_node_t * next; - int t; + /* Place the new handler into the list so that handlers are + popped off in the order given by poporder. */ + _pthread_handler_node_t * new; + _pthread_handler_node_t * next; - t = _pthread_getthreadindex(pthread_self()); + new = (_pthread_handler_node_t *) malloc(sizeof(_pthread_handler_node_t)); - next = (_pthread_cleanup_node_t *) malloc(sizeof(_pthread_cleanup_node_t)); - if (next == NULL) { - /* FIXME: INTERNAL ERROR */ - } + if (new == NULL) + { + /* FIXME: INTERNAL ERROR */ + } - next->next = _pthread_threads_table[t]->cleanupstack->first; - next->routine = routine; - next->arg = arg; - _pthread_threads_table[t]->cleanupstack->first = next; + new->routine = routine; + new->arg = arg; + + if (poporder == _PTHREAD_HANDLER_POP_LIFO) + { + /* Add the new node to the start of the list. */ + new->next = *stacktop; + stacktop = next; + } + else + { + /* Add the new node to the end of the list. */ + new->next = NULL; + + if (*stacktop == NULL) + { + *stacktop = new; + } + else + { + next = *stacktop; + while (next != NULL) + { + next = next->next; + } + next = new; + } + } } void -_pthread_cleanup_pop(int execute) +_pthread_handler_pop(_pthread_handler_node_t ** stacktop, + int execute) { - _pthread_cleanup_node_t * handler; - void (* func)(void *); - void * arg; - int t; - - t = _pthread_getthreadindex(pthread_self()); - handler = _pthread_threads_table[t]->cleanupstack->first; + _pthread_handler_node_t * handler = *stacktop; - if (handler != NULL) { - next = handler->next; - func = handler->routine; - arg = handler->arg; + if (handler != NULL) + { + void (* func)(void *) = handler->routine; + void * arg = handler->arg; - free(handler); + *stacktop = handler->next; - if (execute != 0) - (void) func(arg); + free(handler); - _pthread_threads_table[t]->cleanupstack->first = next; - } + if (execute != 0 && func != NULL) + { + (void) func(arg); + } + } } void -_pthread_do_cancellation(int tindex) +_pthread_handler_pop_all(_pthread_handler_node_t ** stacktop, + int execute) { - _pthread_cleanup_stack_t * stack; - - stack = _pthread_threads_table[tindex]->cleanupstack; - - /* Run all the cleanup handlers */ - while (stack->first != NULL) { - _pthread_cleanup_pop(1); - } + /* Pop and run all handlers on the given stack. */ + while (*stacktop != NULL) + { + _pthread_handler_pop(stacktop, execute); + } } @@ -11,18 +11,34 @@ #include "pthread.h" #include "implement.h" +unsigned +_pthread_start_call(void *this) +{ + /* We're now in a running thread. Any local variables here are on + this threads private stack so we're safe to leave data in them + until we leave. */ + unsigned (*func)(void *) = this->call.routine; + void * arg = this->call.arg; + unsigned ret; + + ret = (*func)(arg); + + /* If we get to here then we're returning naturally and haven't + been cancelled. We need to cleanup and remove the thread + from the threads table. */ + _pthread_vacuum(); + + return ret; +} + int -pthread_create(pthread_t *thread, const pthread_attr_t *attr, - void * (*start_routine) (void *), void * arg) +pthread_create(pthread_t *thread, + const pthread_attr_t *attr, + void * (*start_routine) (void *), + void * arg) { - /* Call Win32 CreateThread. - Map attributes as correctly as possible. - The passed in attr structure will be modified by this routine - to reflect any default values used. This is POSIX semantics. - */ HANDLE handle = NULL; unsigned flags; - unsigned stack; void * security = NULL; /* FIXME: This needs to be moved into process space. @@ -33,109 +49,68 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, */ SECURITY_ATTRIBUTES security_attr; DWORD threadID; - int t; - int ret = 0; /* Success unless otherwise set */ - char * privmem; + /* Success unless otherwise set. */ + int ret = 0; pthread_attr_t * attr_copy; - _pthread_cleanup_stack_t * cleanup_stack; - - /* Use and modify attr_copy. Only after we've succeeded in creating the - new thread can we modify any passed-in structures. - - To save time we use one malloc() to get all of our heap space and - then allocate it further. - */ - - if (NULL == - (privmem = (char) malloc(RND_SIZEOF(pthread_attr_t) + - RND_SIZEOF(_pthread_cleanup_stack_t)))) { - return EAGAIN; - } - - attr_copy = (pthread_attr_t *) privmem; - - /* Force cleanup_stack to start at a DWORD boundary within privmem. - */ - cleanup_stack = - (_pthread_cleanup_stack_t *) &privmem[RND_SIZEOF(pthread_attr_t)]; - - (void) memcpy(attr_copy, attr); + _pthread_threads_thread_t * this; /* CRITICAL SECTION */ pthread_mutex_lock(&_pthread_count_mutex); - if (_pthread_threads_count < PTHREAD_THREADS_MAX) { - switch (attr) - { - case NULL: - /* Use POSIX default attributes */ - stack = attr_copy->stacksize = PTHREAD_STACK_MIN; - break; - default: - /* Map attributes */ - if (attr_copy.stacksize != NULL) - stack = (DWORD) attr_copy->stacksize; - else - stack = attr_copy->stacksize = PTHREAD_STACK_MIN; - break; - } - - flags = 1; /* Start suspended and resume at the last moment to avoid - race conditions, ie. where a thread may enquire it's - attributes before we finish storing them away. - */ - - handle = (HANDLE) _beginthreadex(security, - stack, - (unsigned (_stdcall *)(void *)) start_routine, - arg, - flags, - &threadID); - - if (handle != NULL) { - _pthread_threads_count++; - - /* The hash table works as follows: - hash into the table, - if the slot is occupied then start single stepping from there - until we find an available slot. - */ - t = _PTHREAD_HASH_INDEX(handle); - while ((_pthread_threads_table[t])->thread != NULL) { - t++; - - if (t == PTHREAD_THREADS_MAX) - t = 0; /* Wrap to the top of the table. */ - } - - if ((_pthread_threads_table[t])->thread != NULL) { - /* INTERNAL ERROR */ - } else { - (_pthread_threads_table[t])->thread = handle; - (_pthread_threads_table[t])->attr = attr_copy; - (_pthread_threads_table[t])->cleanupstack = cleanup_stack; - } - } else { + if (_pthread_new_thread_entry((pthread_t) handle, &this) == 0) + { + attr_copy = &(this->attr); + + if (attr != NULL) + { + /* Map attributes */ + if (attr_copy->stacksize == 0) + { + attr_copy->stacksize = PTHREAD_STACK_MIN; + } + + attr_copy->cancelability = attr->cancelability; + } + + /* Start suspended and resume at the last moment to avoid + race conditions, ie. where a thread may enquire it's + attributes before we finish storing them away. */ + flags = 1; + + handle = (HANDLE) _beginthreadex(security, + attr_copy->stacksize, + _pthread_start_call, + (void *) this, + flags, + &threadID); + + if (handle == NULL) + { + ret = EAGAIN; + } + } + else + { ret = EAGAIN; } - } else { - ret = EAGAIN; - } /* Let others in as soon as possible. */ pthread_mutex_unlock(&_pthread_count_mutex); /* END CRITICAL SECTION */ - if (ret == 0) { - *thread = (pthread_t) handle; - (void) memcpy(attr, attr_copy); + if (ret == 0) + { + /* Let the caller know the thread handle. */ + *thread = (pthread_t) handle; - /* POSIX threads are always running after creation. - */ - ResumeThread(handle); - } else { - free(privmem); - } + /* POSIX threads are always running after creation. */ + ResumeThread(handle); + } + else + { + /* Undo everything. */ + _pthread_delete_thread_entry(this); + } return ret; } @@ -12,25 +12,30 @@ #include "implement.h" void -pthread_exit(void * value) +_pthread_vacuum(void) { - int t; - - t = _pthread_getthreadindex(pthread_self()); + /* This function can be called from pthread_exit(), or from + _pthread_start_call() in which case cleanupstack should be + empty but destructorstack still needs to be run. */ + _pthread_threads_thread_t * this; - /* Run all the cleanup handlers */ - _pthread_do_cancellation(t); + this = *_PTHREAD_THIS; - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_count_mutex); + /* Run all the handlers. */ + _pthread_handler_pop_all(&(this->cleanupstack), _PTHREAD_HANDLER_EXECUTE); + _pthread_handler_pop_all(&(this->destructorstack), _PTHREAD_HANDLER_EXECUTE); - /* Frees attr and cleanupstack */ - free(_pthread_threads_table[t]->attr); + /* Pop any atfork handlers to free storage. */ + _pthread_handler_pop_all(&(this->forkprepare), _PTHREAD_HANDLER_NOEXECUTE); + _pthread_handler_pop_all(&(this->forkparent), _PTHREAD_HANDLER_NOEXECUTE); + _pthread_handler_pop_all(&(this->forkchild), _PTHREAD_HANDLER_NOEXECUTE); - _pthread_threads_table[t]->thread = NULL; - - pthread_mutex_unlock(&_pthread_count_mutex); - /* END CRITICAL SECTION */ + _pthread_delete_thread_entry(NULL); +} +void +pthread_exit(void * value) +{ + _pthread_vacuum(); _endthreadex((DWORD) value); } diff --git a/implement.h b/implement.h index 811e717..2256965 100644 --- a/implement.h +++ b/implement.h @@ -7,42 +7,48 @@ #ifndef _IMPLEMENT_H #define _IMPLEMENT_H -/* FIXME: Arbitrary. Need values from Win32. - */ -#define PTHREAD_THREADS_MAX 128 -#define PTHREAD_STACK_MIN 65535 - #define _PTHREAD_HASH_INDEX(x) (((ULONG) x) % PTHREAD_THREADS_MAX) -/* This is all compile time arithmetic */ -#define RND_SIZEOF(T) (((sizeof(T) / sizeof(DWORD)) + 1) * sizeof(DWORD)) +#define _PTHREAD_CANCEL_DEFAULTS \ + (PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED) +/* Handler execution flags. */ +#define _PTHREAD_HANDLER_NOEXECUTE 0 +#define _PTHREAD_HANDLER_EXECUTE 1 -typedef struct _pthread_cleanup_stack _pthread_cleanup_stack_t; -struct _pthread_cleanup_stck { - _pthread_cleanup_stack_t first; - int count; -}; +/* Handler popping schemes. */ +enum { _PTHREAD_HANDLER_POP_LIFO, _PTHREAD_HANDLER_POP_FIFO }; -typedef struct _pthread_cleanup_node _pthread_cleanup_node_t; -struct _pthread_cleanup_node { - _pthread_cleanup_node_t next; - void (* routine)(void *); - void * arg; -}; +/* Special value to mark attribute objects as valid. */ +#define _PTHREAD_ATTR_INVALID 0xC0FFEE + +/* Round a sizeof(type) up to a multiple of sizeof(DWORD). + This is all compile time arithmetic. + */ +#define RND_SIZEOF(T) (((sizeof(T) / sizeof(DWORD)) + 1) * sizeof(DWORD)) #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -void _pthread_cleanup_push(void (*routine)(void *), void *arg); -void _pthread_cleanup_pop(int execute); -void _pthread_do_cancellation(int tindex); +/* Primitives to manage threads table entries. */ + +int _pthread_new_thread_entry(pthread_t thread, + _pthread_threads_thread_t ** entry); + +_pthread_threads_thread ** _pthread_find_thread_entry(pthread_t thread); + +void _pthread_delete_thread_entry(_pthread_threads_thread_t ** this); + +/* Thread cleanup. */ + +void _pthread_vacuum(void); #ifdef __cplusplus } #endif /* __cplusplus */ + #endif /* _IMPLEMENT_H */ @@ -11,32 +11,138 @@ #include "pthread.h" #include "implement.h" +/* The threads table works as follows: + hash into the table, + if the thread in this slot doesn't match then start single + stepping from there until we find it, or we hit an empty slot, or + we end up where we started from. + + The scheme should have these characteristics: + - if the thread handle is a sequence number then the hash will + succeed first time every time, + - if the thread handle is a pseudo randomish value (eg. a pointer) + then the hash should succeed first time most times. + */ + int -_pthread_getthreadindex(pthread_t thread) +_pthread_new_thread_entry(pthread_t thread, + _pthread_threads_thread_t ** entry) { - /* The hash table works as follows: - hash into the table, - if the thread in this slot doesn't match then start single - stepping from there until we find it, or we hit an empty slot, or - we end up where we started from. - - The scheme should have these characteristics: - - if the thread handle is a sequence number then the hash will - succeed first time every time, - - if the thread handle is a pseudo randomish value (eg. a pointer) - then the hash should succeed first time most times. - */ - int t = _PTHREAD_HASH_INDEX(thread); - int it = t; /* Remember where we started from. */ - - while ((_pthread_threads_table[t])->thread != thread) { - t++; - - if (t == PTHREAD_THREADS_MAX) - t = 0; /* Wrap around to the first slot */ - - if ((_pthread_threads_table[t])->thread == NULL || t == it) - return -1; /* Failed to find the thread */ - } - return t; + _pthread_threads_thread_t ** this; + + if (_pthread_threads_count >= PTHREAD_THREADS_MAX) + { + return EAGAIN; + } + + this = &_pthread_threads_table[_PTHREAD_HASH_INDEX(thread)]; + + while ((*this)->thread != NULL) + { + this++; + + if (this == &_pthread_threads_table[PTHREAD_THREADS_MAX]) + { + /* Wrap to the top of the table. */ + this == _pthread_threads_table; + } + } + + if ((*this)->thread != NULL) + { + /* INTERNAL ERROR: There should be at least one slot left. */ + return ESRCH; + } + else + { + new = (_pthread_threads_thread_t *) malloc(sizeof(_pthread_threads_thread_t)); + + if (new == NULL) + { + return ENOMEM; + } + + new->thread = thread; + pthread_attr_init(&(new->attr)); + new->cleanupstack = NULL; + new->destructorstack = NULL; + new->forkpreparestack = NULL; + new->forkparentstack = NULL; + new->forkchildstack = NULL; + *this = new; + } + + _pthread_threads_count++; + entry = this; + + return 0; } + +_pthread_threads_thread ** +_pthread_find_thread_entry(pthread_t thread) +{ + _pthread_threads_thread_t ** this; + _pthread_threads_thread_t ** start; + + start = this = &_pthread_threads_table[_PTHREAD_HASH_INDEX(thread)]; + + while ((*this)->thread != thread) + { + this++; + + if (this == &_pthread_threads_table[PTHREAD_THREADS_MAX]) + { + /* Wrap to top of table. */ + this = _pthread_threads_table; + } + + if ((*this)->thread == NULL || this == start) + { + /* Failed to find the thread. */ + return -1; + } + } + + return this; +} + +void +_pthread_delete_thread_entry(_pthread_threads_thread_t ** this) +{ + /* We don't check that the thread has been properly cleaned up, so + it had better be done already. */ + _pthread_threads_thread ** this; + _pthread_threads_thread ** entry; + + /* CRITICAL SECTION */ + pthread_mutex_lock(&_pthread_count_mutex); + + /* If this is not NULL then we are removing an entry for a + failed thread start. If this is NULL we need to get this + here within the critical section. */ + if (this == NULL) + { + this = _PTHREAD_THIS; + } + + if (this != NULL) + { + entry = this; + /* Do this first to avoid contention and then free the storage. */ + this = NULL; + free(*entry); + + if (_pthread_threads_count > 0) + { + _pthread_threads_count--; + } + else + { + /* FIXME: INTERNAL ERROR: This should not happen. */ + } + } + + pthread_mutex_unlock(&_pthread_count_mutex); + /* END CRITICAL SECTION */ +} + @@ -22,12 +22,39 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef _PTHREADS_H #define _PTHREADS_H +#define PTHREAD_THREADS_MAX 128 +#define PTHREAD_STACK_MIN 65535 + +/* Convert these to defined when implemented. */ +#define _POSIX_THREAD_ATTR_STACKSIZE +#ifdef _POSIX_THREAD_ATTR_STACKADDR +#undef _POSIX_THREAD_ATTR_STACKADDR +#endif + + +/* Cancelability attributes */ +#define PTHREAD_CANCEL_ENABLE 0x00 +#define PTHREAD_CANCEL_DISABLE 0x01 +#define PTHREAD_CANCEL_ASYNCHRONOUS 0x02 +#define PTHREAD_CANCEL_DEFERRED 0x04 + + typedef HANDLE pthread_t; typedef CRITICAL_SECTION pthread_mutex_t; typedef DWORD pthread_key_t; + +/* Related constants */ typedef struct { - size_t stacksize; + long valid; +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + size_t stacksize; /* PTHREAD_STACK_MIN */ +#endif + int cancelability; /* PTHREAD_CANCEL_DISABLE + PTHREAD_CANCEL_ENABLE + PTHREAD_CANCEL_ASYNCHRONOUS + PTHREAD_CANCEL_DEFERRED + _PTHREAD_CANCEL_DEFAULTS */ } pthread_attr_t; typedef struct { @@ -174,57 +201,121 @@ void *pthread_getspecific(pthread_key_t key); int pthread_key_delete(pthread_key_t key); + +/* Internal primitives that must be here. */ + +/* Generic handler push and pop routines. */ + +void _pthread_handler_push(_pthread_handler_node_t ** stacktop, + int poporder, + void (*routine)(void *), + void *arg); + +void _pthread_handler_pop(_pthread_handler_node_t ** stacktop, + int execute); + +void _pthread_handler_pop_all(_pthread_handler_node_t ** stacktop, + int execute); + #ifdef __cplusplus } #endif /* __cplusplus */ + /* The following #defines implement POSIX cleanup handlers. The standard requires that these functions be used as statements and be used pairwise in the same scope. The standard suggests that, in C, they may be implemented as macros starting and ending the same block. + + POSIX requires that applications observe scoping requirements, but + doesn't say if the implemention must enforce them. The macros below + partially enforce scope but can lead to compile or runtime errors. */ +enum { _PTHREAD_HANDLER_POP_LIFO, _PTHREAD_HANDLER_POP_FIFO }; + +#define _PTHREAD_THIS (_pthread_find_thread_entry(pthread_this())) + #ifdef pthread_cleanup_push #undef pthread_cleanup_push #endif #define pthread_cleanup_push(routine, arg) \ { \ - _pthread_cleanup_push(routine, arg); + _pthread_handler_push(&(_PTHREAD_THIS->cleanupstack), \ + _PTHREAD_HANDLER_POP_LIFO, routine, arg); #ifdef pthread_cleanup_pop #undef pthread_cleanup_pop #endif #define pthread_cleanup_pop(execute) \ - _pthread_cleanup_pop(execute);\ + _pthread_handler_pop(&(_PTHREAD_THIS->cleanupstack), execute);\ } -/* Below here goes all internal definitions required by this implementation - of pthreads for Win32. - */ +/************************************************************************** + Below here goes all internal definitions required by this implementation + of pthreads that must be global to any application that uses it. + **************************************************************************/ + +/* General description of a cleanup handler or destructor */ + +typedef struct _pthread_handler_node _pthread_handler_node_t; + +struct _pthread_handler_node { + _pthread_handler_node_t next; + void (* routine)(void *); + void * arg; +}; + +/* Stores a thread call routine and argument. */ +typedef struct { + unsigned (*routine)(void *); + void * arg; +} _pthread_call_t; /* An element in the thread table. */ + typedef struct _pthread_threads_thread _pthread_threads_thread_t; + struct _pthread_threads_thread { - pthread_t thread; - _pthread_attr_t *attr; + pthread_t thread; + pthread_attr_t attr; + _pthread_call_t call; + _pthread_handler_node_t * cleanupstack; + _pthread_handler_node_t * destructorstack; + _pthread_handler_node_t * forkpreparestack; + _pthread_handler_node_t * forkparentstack; + _pthread_handler_node_t * forkchildstack; }; /* _PTHREAD_BUILD_DLL must only be defined if we are building the DLL. */ + #ifndef _PTHREADS_BUILD_DLL -/* Static global data that must be static within the application - but not the DLL. + +/* Global data needed by the application but which must not be static + in the DLL. */ pthread_mutex_t _pthread_count_mutex = PTHREAD_MUTEX_INITIALIZER; + DWORD _pthread_threads_count = 0; -_pthread_threads_thread_t _pthread_threads_table[PTHREAD_THREADS_MAX]; + +_pthread_threads_thread_t * _pthread_threads_table[PTHREAD_THREADS_MAX]; + unsigned short _pthread_once_flag; + pthread_mutex_t _pthread_once_lock = PTHREAD_MUTEX_INITIALIZER; + #else + extern pthread_mutex_t _pthread_count_mutex; + extern DWORD _pthread_threads_count; + extern _pthread_threads_thread_t _pthread_threads_table[]; + extern unsigned short _pthread_once_flag; -pthread_mutex_t _pthread_once_lock; + +extern pthread_mutex_t _pthread_once_lock; + #endif /* End of application static data */ |