From 492c73cf1f1b3e35b394aec991d1201726ec606d Mon Sep 17 00:00:00 2001 From: rpj Date: Tue, 21 Jul 1998 17:04:38 +0000 Subject: Wed Jul 22 00:16:22 1998 Ross Johnson * cleanup.c (_pthread_cleanup_push): Implement. (_pthread_cleanup_pop): Implement. (_pthread_do_cancellation): Implement. These are private to the implementation. The real cleanup functions are macros. See below. * pthread.h (pthread_cleanup_push): Implement as a macro. (pthread_cleanup_pop): Implement as a macro. Because these are macros which start and end a block, the POSIX scoping requirement is observed. See the comment in the file. * exit.c (pthread_exit): Refine the code. * create.c (pthread_create): Code cleanup. * implement.h (RND_SIZEOF): Add RND_SIZEOF(T) to round sizeof(T) up to multiple of DWORD. Add function prototypes. * private.c (_pthread_getthreadindex): "*thread" should have been "thread". Detect empty slot fail condition. --- ChangeLog | 24 ++++++++++++++++++++++ cleanup.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ create.c | 34 +++++++++++++++---------------- exit.c | 24 ++++++++-------------- implement.h | 16 +++++++++++++++ private.c | 11 ++++++---- pthread.h | 20 ++++++++++++++++++ 7 files changed, 158 insertions(+), 38 deletions(-) create mode 100644 cleanup.c diff --git a/ChangeLog b/ChangeLog index 6a1b02f..ba6e725 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +Wed Jul 22 00:16:22 1998 Ross Johnson + + * cleanup.c (_pthread_cleanup_push): Implement. + (_pthread_cleanup_pop): Implement. + (_pthread_do_cancellation): Implement. + These are private to the implementation. The real cleanup functions + are macros. See below. + + * pthread.h (pthread_cleanup_push): Implement as a macro. + (pthread_cleanup_pop): Implement as a macro. + Because these are macros which start and end a block, the POSIX scoping + requirement is observed. See the comment in the file. + + * exit.c (pthread_exit): Refine the code. + + * create.c (pthread_create): Code cleanup. + + * implement.h (RND_SIZEOF): Add RND_SIZEOF(T) to round sizeof(T) + up to multiple of DWORD. + Add function prototypes. + + * private.c (_pthread_getthreadindex): "*thread" should have been + "thread". Detect empty slot fail condition. + 1998-07-20 Ben Elliston * misc.c (pthread_once): Implement. Don't use a per-application diff --git a/cleanup.c b/cleanup.c new file mode 100644 index 0000000..f670b97 --- /dev/null +++ b/cleanup.c @@ -0,0 +1,67 @@ +/* + * cleanup.c + * + * Description: + * This translation unit implements routines associated cleaning up + * threads. + */ + +#include "pthread.h" +#include "implement.h" + +void +_pthread_cleanup_push(void (*routine)(void *), void *arg) +{ + _pthread_cleanup_node_t * next; + int t; + + t = _pthread_getthreadindex(pthread_self()); + + next = (_pthread_cleanup_node_t *) malloc(sizeof(_pthread_cleanup_node_t)); + if (next == 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; +} + +void +_pthread_cleanup_pop(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; + + if (handler != NULL) { + next = handler->next; + func = handler->routine; + arg = handler->arg; + + free(handler); + + if (execute != 0) + (void) func(arg); + + _pthread_threads_table[t]->cleanupstack->first = next; + } +} + +void +_pthread_do_cancellation(int tindex) +{ + _pthread_cleanup_stack_t * stack; + + stack = _pthread_threads_table[tindex]->cleanupstack; + + /* Run all the cleanup handlers */ + while (stack->first != NULL) { + _pthread_cleanup_pop(1); + } +} diff --git a/create.c b/create.c index 6897a9f..3814a23 100644 --- a/create.c +++ b/create.c @@ -42,26 +42,24 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, /* Use and modify attr_copy. Only after we've succeeded in creating the new thread can we modify any passed-in structures. - We use one malloc() to get all of our heap space and then allocate - it further. + To save time 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)))) { + + 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 the next to a DWORD boundary. - This is all compile time arithmetic. + + /* Force cleanup_stack to start at a DWORD boundary within privmem. */ cleanup_stack = - (_pthread_cleanup_stack_t *) &privmem[((sizeof(pthread_attr_t) / - sizeof(DWORD)) + 1) - * sizeof(DWORD)]; + (_pthread_cleanup_stack_t *) &privmem[RND_SIZEOF(pthread_attr_t)]; (void) memcpy(attr_copy, attr); - /* CRITICAL SECTION */ pthread_mutex_lock(&_pthread_count_mutex); @@ -82,7 +80,10 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, break; } - flags = 1; /* Start suspended and resume at the last moment */ + 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, @@ -92,22 +93,21 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, &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 */ + t = 0; /* Wrap to the top of the table. */ } + if ((_pthread_threads_table[t])->thread != NULL) { /* INTERNAL ERROR */ } else { @@ -131,8 +131,6 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, (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 { diff --git a/exit.c b/exit.c index a0b3b50..0778fc2 100644 --- a/exit.c +++ b/exit.c @@ -6,37 +6,29 @@ * a thread. */ +#include +#include #include "pthread.h" +#include "implement.h" void pthread_exit(void * value) { - /* The semantics are such that additional tasks must be done for - 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; 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); - } + _pthread_do_cancellation(t); /* CRITICAL SECTION */ pthread_mutex_lock(&_pthread_count_mutex); + + /* Frees attr and cleanupstack */ free(_pthread_threads_table[t]->attr); + _pthread_threads_table[t]->thread = NULL; + pthread_mutex_unlock(&_pthread_count_mutex); /* END CRITICAL SECTION */ diff --git a/implement.h b/implement.h index 2e03c48..2a7e192 100644 --- a/implement.h +++ b/implement.h @@ -14,6 +14,10 @@ #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)) + + typedef struct _pthread_cleanup_stack _pthread_cleanup_stack_t; struct _pthread_cleanup_stck { _pthread_cleanup_stack_t first; @@ -39,6 +43,18 @@ typedef struct { /* Nothing needed yet. */ } _pthread_condattr_t; +#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); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* _IMPLEMENT_H */ diff --git a/private.c b/private.c index a880042..9499e07 100644 --- a/private.c +++ b/private.c @@ -17,7 +17,8 @@ _pthread_getthreadindex(pthread_t thread) /* 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. + 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 @@ -25,14 +26,16 @@ _pthread_getthreadindex(pthread_t thread) - 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; /* The initial thread index */ + 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 (t == it) + + if ((_pthread_threads_table[t])->thread == NULL || t == it) return -1; /* Failed to find the thread */ } return t; diff --git a/pthread.h b/pthread.h index 974a163..bf9ff7b 100644 --- a/pthread.h +++ b/pthread.h @@ -170,6 +170,26 @@ int pthread_key_delete(pthread_key_t key); } #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. + */ +#ifdef pthread_cleanup_push +#undef pthread_cleanup_push +#endif +#define pthread_cleanup_push(routine, arg) \ +{ \ + _pthread_cleanup_push(routine, arg); + +#ifdef pthread_cleanup_pop +#undef pthread_cleanup_pop +#endif +#define pthread_cleanup_pop(execute) \ + _pthread_cleanup_pop(execute);\ +} + + /* Below here goes all internal definitions required by this implementation of pthreads for Win32. */ -- cgit v1.2.3