From 40cf527fe65e12a745ca7b981676da1fb691eee6 Mon Sep 17 00:00:00 2001 From: rpj Date: Sun, 19 Jul 1998 16:48:18 +0000 Subject: Mon Jul 20 02:31:05 1998 Ross Johnson * private.c (_pthread_getthreadindex): Implement. * pthread.h: Add application static data dependent on _PTHREADS_BUILD_DLL define. This is needed to avoid allocating non-sharable static data within the pthread DLL. * implement.h: Add _pthread_cleanup_stack_t, _pthread_cleanup_node_t and _PTHREAD_HASH_INDEX. * exit.c (pthread_exit): Begin work on cleanup and de-allocate thread-private storage. * create.c (pthread_create): Add thread to thread table. Keep a thread-private copy of the attributes with default values filled in when necessary. Same for the cleanup stack. Make pthread_create C run-time library friendly by using _beginthreadex() instead of CreateThread(). Fix error returns. Sun Jul 19 16:26:23 1998 Ross Johnson * implement.h: Rename pthreads_thread_count to _pthread_threads_count. Create _pthread_threads_thread_t struct to keep thread specific data. * create.c: Rename pthreads_thread_count to _pthread_threads_count. (pthread_create): Handle errors from CreateThread(). --- ChangeLog | 28 ++++++++++++ create.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++-------------- exit.c | 26 ++++++++++- implement.h | 19 +++++++- pthread.h | 27 ++++++++++++ 5 files changed, 207 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index 53cd93f..e0846aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +Mon Jul 20 02:31:05 1998 Ross Johnson + + * private.c (_pthread_getthreadindex): Implement. + + * pthread.h: Add application static data dependent on + _PTHREADS_BUILD_DLL define. This is needed to avoid allocating + non-sharable static data within the pthread DLL. + + * implement.h: Add _pthread_cleanup_stack_t, _pthread_cleanup_node_t + and _PTHREAD_HASH_INDEX. + + * exit.c (pthread_exit): Begin work on cleanup and de-allocate + thread-private storage. + + * create.c (pthread_create): Add thread to thread table. + Keep a thread-private copy of the attributes with default values + filled in when necessary. Same for the cleanup stack. Make + pthread_create C run-time library friendly by using _beginthreadex() + instead of CreateThread(). Fix error returns. + +Sun Jul 19 16:26:23 1998 Ross Johnson + + * implement.h: Rename pthreads_thread_count to _pthread_threads_count. + Create _pthread_threads_thread_t struct to keep thread specific data. + + * create.c: Rename pthreads_thread_count to _pthread_threads_count. + (pthread_create): Handle errors from CreateThread(). + 1998-07-19 Ben Elliston * condvar.c (pthread_cond_wait): Generalise. Moved from here .. diff --git a/create.c b/create.c index 7a374bb..6897a9f 100644 --- a/create.c +++ b/create.c @@ -6,23 +6,24 @@ * thread. */ +#include +#include #include "pthread.h" #include "implement.h" -/* FIXME: There must be a Win32 routine to get this value. */ -DWORD pthread_threads_count = 0; - int 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; - DWORD flags = 0; /* Always 0 for POSIX threads */ - DWORD stack = PTHREAD_STACK_MIN; - LPSECURITY_ATTRIBUTES security = NULL; + HANDLE handle = NULL; + unsigned flags; + unsigned stack; + void * security = NULL; /* FIXME: This needs to be moved into process space. Perhaps into a structure that contains all @@ -32,34 +33,111 @@ 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; + 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. - if (pthread_threads_count >= PTHREAD_THREADS_MAX) + We use one malloc() to get all of our heap space and then allocate + it further. + */ + if (NULL == (privmem = (char) malloc(sizeof(pthread_attr_t) + + sizeof(_pthread_cleanup_stack_t) + + sizeof(DWORD)))) { return EAGAIN; + } + + attr_copy = (pthread_attr_t *) privmem; + /* Force the next to a DWORD boundary. + This is all compile time arithmetic. + */ + cleanup_stack = + (_pthread_cleanup_stack_t *) &privmem[((sizeof(pthread_attr_t) / + sizeof(DWORD)) + 1) + * sizeof(DWORD)]; + + (void) memcpy(attr_copy, attr); + + + /* 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 */ - switch (attr) - { - case NULL: - /* Use POSIX default attributes */ - break; - default: - /* Map attributes */ - if (attr->stacksize != NULL) - stack = (DWORD) attr->stacksize; - break; + handle = (HANDLE) _beginthreadex(security, + stack, + (unsigned (_stdcall *)(void *)) start_routine, + arg, + flags, + &threadID); + + if (handle != NULL) { + /* We have a new thread */ + _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. + + There is at least one slot available at this point. + */ + t = _PTHREAD_HASH_INDEX(handle); + while ((_pthread_threads_table[t])->thread != NULL) { + t++; + if (t == PTHREAD_THREADS_MAX) + t = 0; /* Wrap around to the first slot */ + } + 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 { + ret = EAGAIN; } + } else { + ret = EAGAIN; + } - /* FIXME: I don't have error return values to work with so - I'm assuming this always succeeds (obviously not). - */ - handle = CreateThread(security, - stack, - start_routine, - arg, - flags, - &threadID); - - *thread = (pthread_t) handle; - pthread_threads_count++; - return 0; -} + /* 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); + /* POSIX threads are always running after creation. + Start the thread only after passed-in structures have been + modified to avoid race conditions. + */ + ResumeThread(handle); + } else { + free(privmem); + } + + return ret; +} diff --git a/exit.c b/exit.c index 5212854..a0b3b50 100644 --- a/exit.c +++ b/exit.c @@ -15,6 +15,30 @@ pthread_exit(void * value) strict POSIX conformance. We must add code here later which deals with executing cleanup handlers and such. For now, the following is mostly correct: */ + int t; - ExitThread((DWORD) value); + t = _pthread_getthreadindex(pthread_self()); + handler = _pthread_threads_table[t]->cleanupstack->first; + + /* Run all the cleanup handlers */ + while (handler != NULL) { + void (* func)(void *); + void * arg; + _pthread_cleanup_node_t * next; + + func = handler->routine; + arg = handler->arg; + _pthread_threads_table[t]->cleanupstack->first = next = handler->next; + free(handler); + (void) func(arg); + } + + /* CRITICAL SECTION */ + pthread_mutex_lock(&_pthread_count_mutex); + free(_pthread_threads_table[t]->attr); + _pthread_threads_table[t]->thread = NULL; + pthread_mutex_unlock(&_pthread_count_mutex); + /* END CRITICAL SECTION */ + + _endthreadex((DWORD) value); } diff --git a/implement.h b/implement.h index 254d86f..2e03c48 100644 --- a/implement.h +++ b/implement.h @@ -9,10 +9,23 @@ /* FIXME: Arbitrary. Need values from Win32. */ -#define PTHREAD_THREADS_MAX 256 +#define PTHREAD_THREADS_MAX 128 #define PTHREAD_STACK_MIN 65535 -extern DWORD pthreads_thread_count; +#define _PTHREAD_HASH_INDEX(x) (((ULONG) x) % PTHREAD_THREADS_MAX) + +typedef struct _pthread_cleanup_stack _pthread_cleanup_stack_t; +struct _pthread_cleanup_stck { + _pthread_cleanup_stack_t first; + int count; +}; + +typedef struct _pthread_cleanup_node _pthread_cleanup_node_t; +struct _pthread_cleanup_node { + _pthread_cleanup_node_t next; + void (* routine)(void *); + void * arg; +}; typedef struct { size_t stacksize; @@ -27,3 +40,5 @@ typedef struct { } _pthread_condattr_t; #endif /* _IMPLEMENT_H */ + + diff --git a/pthread.h b/pthread.h index 8137f53..abce374 100644 --- a/pthread.h +++ b/pthread.h @@ -163,4 +163,31 @@ int pthread_key_delete(pthread_key_t key); } #endif /* __cplusplus */ +/* Below here goes all internal definitions required by this implementation + of pthreads for Win32. + */ + +/* 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_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. + */ +pthread_mutex_t _pthread_count_mutex = PTHREAD_MUTEX_INITIALIZER; +DWORD _pthread_threads_count = 0; +_pthread_threads_thread_t _pthread_threads_table[PTHREAD_THREADS_MAX]; +#else +extern pthread_mutex_t _pthread_count_mutex; +extern DWORD _pthread_threads_count; +extern _pthread_threads_thread_t _pthread_threads_table[]; +#endif + +/* End of application static data */ + #endif /* _PTHREADS_H */ -- cgit v1.2.3