From 3387a425e65b839b68bd2973f6bc5ab22315cc5d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 25 Jun 1998 19:36:00 +0000 Subject: [PATCH] Finish user stack support. Change locking code to be safe in situations with different priorities. 1998-06-25 19:27 Ulrich Drepper * attr.c: Finish user stack support. Change locking code to be safe in situations with different priorities. * cancel.c: Likewise. * condvar.c: Likewise. * internals.h: Likewise. * join.c: Likewise. * manager.c: Likewise. * mutex.c: Likewise. * pthread.c: Likewise. * ptlongjmp.c: Likewise. * queue.h: Likewise. * rwlock.c: Likewise. * semaphore.c: Likewise. * semaphore.h: Likewise. * signals.c: Likewise. * spinlock.c: Likewise. * spinlock.h: Likewise. Patches by Xavier leroy. 1998-06-25 Ulrich Drepper * sysdeps/pthread/pthread.h: Make [sg]et_stacksize and [sg]et_stackaddr prototypes always available. * sysdeps/unix/sysv/linux/bits/posix_opt.h: Define _POSIX_THREAD_ATTR_STACKSIZE and _POSIX_THREAD_ATTR_STACKADDR. --- Makeconfig | 5 +- libc.map | 3 +- linuxthreads/ChangeLog | 29 +++ linuxthreads/attr.c | 8 +- linuxthreads/cancel.c | 6 +- linuxthreads/condvar.c | 79 ++---- linuxthreads/internals.h | 28 ++- linuxthreads/join.c | 28 +-- linuxthreads/manager.c | 79 ++++-- linuxthreads/mutex.c | 122 +++------ linuxthreads/pthread.c | 89 +++++-- linuxthreads/ptlongjmp.c | 2 + linuxthreads/queue.h | 57 ++--- linuxthreads/rwlock.c | 47 ++-- linuxthreads/semaphore.c | 232 +++++------------- linuxthreads/semaphore.h | 7 +- linuxthreads/signals.c | 89 +++---- linuxthreads/spinlock.c | 123 +++++++++- linuxthreads/spinlock.h | 53 +++- linuxthreads/sysdeps/i386/pt-machine.h | 6 +- linuxthreads/sysdeps/pthread/pthread.h | 38 +-- .../sysdeps/unix/sysv/linux/bits/posix_opt.h | 8 +- sysdeps/unix/sysv/linux/bits/sched.h | 7 +- 23 files changed, 633 insertions(+), 512 deletions(-) diff --git a/Makeconfig b/Makeconfig index d70c4795ef..1ef0cc571d 100644 --- a/Makeconfig +++ b/Makeconfig @@ -564,8 +564,9 @@ endif # $(+cflags) == "" libio-include = -I$(..)libio # These are the variables that the implicit compilation rules use. -CPPFLAGS = $(+includes) $(defines) -include $(..)include/libc-symbols.h \ - $(sysdep-CPPFLAGS) $(CPPFLAGS-$(suffix $@)) $(CPPFLAGS-$( + + * attr.c: Finish user stack support. Change locking code to be safe + in situations with different priorities. + * cancel.c: Likewise. + * condvar.c: Likewise. + * internals.h: Likewise. + * join.c: Likewise. + * manager.c: Likewise. + * mutex.c: Likewise. + * pthread.c: Likewise. + * ptlongjmp.c: Likewise. + * queue.h: Likewise. + * rwlock.c: Likewise. + * semaphore.c: Likewise. + * semaphore.h: Likewise. + * signals.c: Likewise. + * spinlock.c: Likewise. + * spinlock.h: Likewise. + Patches by Xavier leroy. + +1998-06-25 Ulrich Drepper + + * sysdeps/pthread/pthread.h: Make [sg]et_stacksize and + [sg]et_stackaddr prototypes always available. + + * sysdeps/unix/sysv/linux/bits/posix_opt.h: Define + _POSIX_THREAD_ATTR_STACKSIZE and _POSIX_THREAD_ATTR_STACKADDR. + 1998-06-24 Ulrich Drepper * manager.c (pthread_free): Undo patch from 980430. diff --git a/linuxthreads/attr.c b/linuxthreads/attr.c index 027f69c997..d1f605810f 100644 --- a/linuxthreads/attr.c +++ b/linuxthreads/attr.c @@ -19,7 +19,6 @@ #include "pthread.h" #include "internals.h" - int __pthread_attr_init_2_1(pthread_attr_t *attr) { size_t ps = __getpagesize (); @@ -185,11 +184,8 @@ weak_alias (__pthread_attr_getstackaddr, pthread_attr_getstackaddr) int __pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { - size_t ps = __getpagesize (); - - /* We don't accept value smaller than PTHREAD_STACK_MIN or bigger than - 2MB - pagesize. */ - if (stacksize < PTHREAD_STACK_MIN || stacksize > STACK_SIZE - ps) + /* We don't accept value smaller than PTHREAD_STACK_MIN. */ + if (stacksize < PTHREAD_STACK_MIN) return EINVAL; attr->stacksize = stacksize; diff --git a/linuxthreads/cancel.c b/linuxthreads/cancel.c index a6a0ecaa5a..cd2bbace12 100644 --- a/linuxthreads/cancel.c +++ b/linuxthreads/cancel.c @@ -53,14 +53,14 @@ int pthread_cancel(pthread_t thread) pthread_handle handle = thread_handle(thread); int pid; - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); if (invalid_handle(handle, thread)) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return ESRCH; } handle->h_descr->p_canceled = 1; pid = handle->h_descr->p_pid; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); kill(pid, PTHREAD_SIG_CANCEL); return 0; } diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c index 6807522edc..91067d2d17 100644 --- a/linuxthreads/condvar.c +++ b/linuxthreads/condvar.c @@ -25,42 +25,36 @@ #include "queue.h" #include "restart.h" -static void remove_from_queue(pthread_queue * q, pthread_descr th); - int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) { - cond->c_spinlock = 0; - queue_init(&cond->c_waiting); + __pthread_init_lock(&cond->c_lock); + cond->c_waiting = NULL; return 0; } int pthread_cond_destroy(pthread_cond_t *cond) { - pthread_descr head; - - acquire(&cond->c_spinlock); - head = cond->c_waiting.head; - release(&cond->c_spinlock); - if (head != NULL) return EBUSY; + if (cond->c_waiting != NULL) return EBUSY; return 0; } int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { volatile pthread_descr self = thread_self(); - acquire(&cond->c_spinlock); + + __pthread_lock(&cond->c_lock); enqueue(&cond->c_waiting, self); - release(&cond->c_spinlock); + __pthread_unlock(&cond->c_lock); pthread_mutex_unlock(mutex); suspend_with_cancellation(self); pthread_mutex_lock(mutex); /* This is a cancellation point */ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { /* Remove ourselves from the waiting queue if we're still on it */ - acquire(&cond->c_spinlock); + __pthread_lock(&cond->c_lock); remove_from_queue(&cond->c_waiting, self); - release(&cond->c_spinlock); + __pthread_unlock(&cond->c_lock); pthread_exit(PTHREAD_CANCELED); } return 0; @@ -77,15 +71,14 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond, sigjmp_buf jmpbuf; /* Wait on the condition */ - acquire(&cond->c_spinlock); + __pthread_lock(&cond->c_lock); enqueue(&cond->c_waiting, self); - release(&cond->c_spinlock); + __pthread_unlock(&cond->c_lock); pthread_mutex_unlock(mutex); - /* Set up a longjmp handler for the restart signal */ - /* No need to save the signal mask, since PTHREAD_SIG_RESTART will be - blocked when doing the siglongjmp, and we'll just leave it blocked. */ - if (sigsetjmp(jmpbuf, 0) == 0) { + /* Set up a longjmp handler for the restart and cancel signals */ + if (sigsetjmp(jmpbuf, 1) == 0) { self->p_signal_jmp = &jmpbuf; + self->p_cancel_jmp = &jmpbuf; self->p_signal = 0; /* Check for cancellation */ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { @@ -104,28 +97,28 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond, retsleep = -1; } self->p_signal_jmp = NULL; + self->p_cancel_jmp = NULL; /* Here, either the condition was signaled (self->p_signal != 0) or we got canceled (self->p_canceled != 0) or the timeout occurred (retsleep == 0) or another interrupt occurred (retsleep == -1) */ - /* Re-acquire the spinlock */ - acquire(&cond->c_spinlock); /* This is a cancellation point */ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { + __pthread_lock(&cond->c_lock); remove_from_queue(&cond->c_waiting, self); - release(&cond->c_spinlock); + __pthread_unlock(&cond->c_lock); pthread_mutex_lock(mutex); pthread_exit(PTHREAD_CANCELED); } /* If not signaled: also remove ourselves and return an error code */ if (self->p_signal == 0) { + __pthread_lock(&cond->c_lock); remove_from_queue(&cond->c_waiting, self); - release(&cond->c_spinlock); + __pthread_unlock(&cond->c_lock); pthread_mutex_lock(mutex); return retsleep == 0 ? ETIMEDOUT : EINTR; } /* Otherwise, return normally */ - release(&cond->c_spinlock); pthread_mutex_lock(mutex); return 0; } @@ -151,23 +144,22 @@ int pthread_cond_signal(pthread_cond_t *cond) { pthread_descr th; - acquire(&cond->c_spinlock); + __pthread_lock(&cond->c_lock); th = dequeue(&cond->c_waiting); - release(&cond->c_spinlock); + __pthread_unlock(&cond->c_lock); if (th != NULL) restart(th); return 0; } int pthread_cond_broadcast(pthread_cond_t *cond) { - pthread_queue tosignal; - pthread_descr th; + pthread_descr tosignal, th; - acquire(&cond->c_spinlock); + __pthread_lock(&cond->c_lock); /* Copy the current state of the waiting queue and empty it */ tosignal = cond->c_waiting; - queue_init(&cond->c_waiting); - release(&cond->c_spinlock); + cond->c_waiting = NULL; + __pthread_unlock(&cond->c_lock); /* Now signal each process in the queue */ while ((th = dequeue(&tosignal)) != NULL) restart(th); return 0; @@ -182,26 +174,3 @@ int pthread_condattr_destroy(pthread_condattr_t *attr) { return 0; } - -/* Auxiliary function on queues */ - -static void remove_from_queue(pthread_queue * q, pthread_descr th) -{ - pthread_descr t; - - if (q->head == NULL) return; - if (q->head == th) { - q->head = th->p_nextwaiting; - if (q->head == NULL) q->tail = NULL; - th->p_nextwaiting = NULL; - return; - } - for (t = q->head; t->p_nextwaiting != NULL; t = t->p_nextwaiting) { - if (t->p_nextwaiting == th) { - t->p_nextwaiting = th->p_nextwaiting; - if (th->p_nextwaiting == NULL) q->tail = t; - th->p_nextwaiting = NULL; - return; - } - } -} diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h index ab6b66a857..0a01b61ede 100644 --- a/linuxthreads/internals.h +++ b/linuxthreads/internals.h @@ -63,7 +63,7 @@ struct _pthread_descr_struct { pthread_t p_tid; /* Thread identifier */ int p_pid; /* PID of Unix process */ int p_priority; /* Thread priority (== 0 if not realtime) */ - int * p_spinlock; /* Spinlock for synchronized accesses */ + struct _pthread_fastlock * p_lock; /* Spinlock for synchronized accesses */ int p_signal; /* last signal received */ sigjmp_buf * p_signal_jmp; /* where to siglongjmp on a signal or NULL */ sigjmp_buf * p_cancel_jmp; /* where to siglongjmp on a cancel or NULL */ @@ -81,6 +81,8 @@ struct _pthread_descr_struct { int p_errno; /* error returned by last system call */ int * p_h_errnop; /* pointer to used h_errno variable */ int p_h_errno; /* error returned by last netdb function */ + char * p_in_sighandler; /* stack address of sighandler, or NULL */ + char p_sigwaiting; /* true if a sigwait() is in progress */ struct pthread_start_args p_start_args; /* arguments for thread creation */ void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE]; /* thread-specific data */ void * p_libc_specific[_LIBC_TSD_KEY_N]; /* thread-specific data for libc */ @@ -94,8 +96,9 @@ struct _pthread_descr_struct { typedef struct pthread_handle_struct * pthread_handle; struct pthread_handle_struct { - int h_spinlock; /* Spinlock for sychronized access */ + struct _pthread_fastlock h_lock; /* Fast lock for sychronized access */ pthread_descr h_descr; /* Thread descriptor or NULL if invalid */ + char * h_bottom; /* Lowest address in the stack thread */ }; /* The type of messages sent to the thread manager thread */ @@ -103,7 +106,8 @@ struct pthread_handle_struct { struct pthread_request { pthread_descr req_thread; /* Thread doing the request */ enum { /* Request kind */ - REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT + REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT, + REQ_POST, REQ_DEBUG } req_kind; union { /* Arguments for request */ struct { /* For REQ_CREATE: */ @@ -118,6 +122,7 @@ struct pthread_request { struct { /* For REQ_PROCESS_EXIT: */ int code; /* exit status */ } exit; + void * post; /* For REQ_POST: the semaphore */ } req_args; }; @@ -160,6 +165,11 @@ extern pthread_descr __pthread_main_thread; extern char *__pthread_initial_thread_bos; +/* Indicate whether at least one thread has a user-defined stack (if 1), + or all threads have stacks supplied by LinuxThreads (if 0). */ + +extern int __pthread_nonstandard_stacks; + /* File descriptor for sending requests to the thread manager. Initially -1, meaning that pthread_initialize must be called. */ @@ -178,6 +188,10 @@ extern char *__pthread_manager_thread_tos; extern int __pthread_exit_requested, __pthread_exit_code; +/* Set to 1 by gdb if we're debugging */ + +extern volatile int __pthread_threads_debug; + /* Return the handle corresponding to a thread id */ static inline pthread_handle thread_handle(pthread_t id) @@ -233,6 +247,8 @@ static inline int invalid_handle(pthread_handle h, pthread_t id) /* Recover thread descriptor for the current thread */ +extern pthread_descr __pthread_find_self (void) __attribute__ ((const)); + static inline pthread_descr thread_self (void) __attribute__ ((const)); static inline pthread_descr thread_self (void) { @@ -245,6 +261,8 @@ static inline pthread_descr thread_self (void) else if (sp >= __pthread_manager_thread_bos && sp < __pthread_manager_thread_tos) return &__pthread_manager_thread; + else if (__pthread_nonstandard_stacks) + return __pthread_find_self(); else return (pthread_descr)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1; #endif @@ -282,8 +300,8 @@ static inline pthread_descr thread_self (void) void __pthread_destroy_specifics(void); void __pthread_perform_cleanup(void); -void __pthread_sighandler(int sig); -void __pthread_message(char * fmt, long arg, ...); +int __pthread_initialize_manager(void); +void __pthread_message(char * fmt, ...); int __pthread_manager(void *reqfd); void __pthread_manager_sighandler(int sig); void __pthread_reset_main_thread(void); diff --git a/linuxthreads/join.c b/linuxthreads/join.c index 2bdc189631..c59de6985a 100644 --- a/linuxthreads/join.c +++ b/linuxthreads/join.c @@ -35,13 +35,13 @@ void pthread_exit(void * retval) __pthread_perform_cleanup(); __pthread_destroy_specifics(); /* Store return value */ - acquire(self->p_spinlock); + __pthread_lock(self->p_lock); self->p_retval = retval; /* Say that we've terminated */ self->p_terminated = 1; /* See if someone is joining on us */ joining = self->p_joining; - release(self->p_spinlock); + __pthread_unlock(self->p_lock); /* Restart joining thread if any */ if (joining != NULL) restart(joining); /* If this is the initial thread, block until all threads have terminated. @@ -65,36 +65,36 @@ int pthread_join(pthread_t thread_id, void ** thread_return) pthread_handle handle = thread_handle(thread_id); pthread_descr th; - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); if (invalid_handle(handle, thread_id)) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return ESRCH; } th = handle->h_descr; if (th == self) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return EDEADLK; } /* If detached or already joined, error */ if (th->p_detached || th->p_joining != NULL) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return EINVAL; } /* If not terminated yet, suspend ourselves. */ if (! th->p_terminated) { th->p_joining = self; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); suspend_with_cancellation(self); /* This is a cancellation point */ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { th->p_joining = NULL; pthread_exit(PTHREAD_CANCELED); } - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); } /* Get return value */ if (thread_return != NULL) *thread_return = th->p_retval; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); /* Send notification to thread manager */ if (__pthread_manager_request >= 0) { request.req_thread = self; @@ -113,26 +113,26 @@ int pthread_detach(pthread_t thread_id) pthread_handle handle = thread_handle(thread_id); pthread_descr th; - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); if (invalid_handle(handle, thread_id)) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return ESRCH; } th = handle->h_descr; /* If already detached, error */ if (th->p_detached) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return EINVAL; } /* If already joining, don't do anything. */ if (th->p_joining != NULL) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return 0; } /* Mark as detached */ th->p_detached = 1; terminated = th->p_terminated; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); /* If already terminated, notify thread manager to reclaim resources */ if (terminated && __pthread_manager_request >= 0) { request.req_thread = thread_self(); diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c index ac78d6e4ce..36c592197e 100644 --- a/linuxthreads/manager.c +++ b/linuxthreads/manager.c @@ -31,15 +31,22 @@ #include "internals.h" #include "spinlock.h" #include "restart.h" +#include "semaphore.h" /* Array of active threads. Entry 0 is reserved for the initial thread. */ - struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] = -{ { 0, &__pthread_initial_thread, 0}, /* All NULLs */ }; +{ { LOCK_INITIALIZER, &__pthread_initial_thread, 0}, /* All NULLs */ }; /* Indicate whether at least one thread has a user-defined stack (if 1), or if all threads have stacks supplied by LinuxThreads (if 0). */ -int __pthread_nonstandard_stacks; +int __pthread_nonstandard_stacks = 0; + +/* Number of active entries in __pthread_handles (used by gdb) */ +volatile int __pthread_handles_num = 1; + +/* Whether to use debugger additional actions for thread creation + (set to 1 by gdb) */ +volatile int __pthread_threads_debug = 0; /* Mapping from stack segment to thread descriptor. */ /* Stack segment numbers are also indices into the __pthread_handles array. */ @@ -93,12 +100,18 @@ int __pthread_manager(void *arg) /* Set the error variable. */ __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno; __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno; - /* Block all signals except PTHREAD_SIG_RESTART */ + /* Block all signals except PTHREAD_SIG_RESTART, PTHREAD_SIG_CANCEL + and SIGTRAP */ sigfillset(&mask); sigdelset(&mask, PTHREAD_SIG_RESTART); + sigdelset(&mask, PTHREAD_SIG_CANCEL); /* for debugging new threads */ + sigdelset(&mask, SIGTRAP); /* for debugging purposes */ sigprocmask(SIG_SETMASK, &mask, NULL); /* Raise our priority to match that of main thread */ __pthread_manager_adjust_prio(__pthread_main_thread->p_priority); + /* Synchronize debugging of the thread manager */ + n = __libc_read(reqfd, (char *)&request, sizeof(request)); + ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG); /* Enter server loop */ while(1) { FD_ZERO(&readfds); @@ -146,6 +159,14 @@ int __pthread_manager(void *arg) return 0; } break; + case REQ_POST: + sem_post(request.req_args.post); + break; + case REQ_DEBUG: + /* Make gdb aware of new thread */ + if (__pthread_threads_debug) raise(PTHREAD_SIG_CANCEL); + restart(request.req_thread); + break; } } } @@ -156,6 +177,7 @@ int __pthread_manager(void *arg) static int pthread_start_thread(void *arg) { pthread_descr self = (pthread_descr) arg; + struct pthread_request request; void * outcome; /* Initialize special thread_self processing, if any. */ #ifdef INIT_THREAD_SELF @@ -171,6 +193,14 @@ static int pthread_start_thread(void *arg) if (self->p_start_args.schedpolicy >= 0) __sched_setscheduler(self->p_pid, self->p_start_args.schedpolicy, &self->p_start_args.schedparam); + /* Make gdb aware of new thread */ + if (__pthread_threads_debug) { + request.req_thread = self; + request.req_kind = REQ_DEBUG; + __libc_write(__pthread_manager_request, + (char *) &request, sizeof(request)); + suspend(self); + } /* Run the thread code */ outcome = self->p_start_args.start_routine(self->p_start_args.arg); /* Exit with the given return value */ @@ -188,6 +218,7 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, char * new_thread_bottom; pthread_t new_thread_id; void *guardaddr = NULL; + size_t guardsize = 0; /* Find a free stack segment for the current stack */ for (sseg = 1; ; sseg++) @@ -211,13 +242,16 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, and allocate it if necessary. */ if (attr == NULL || attr->guardsize != 0) { + guardsize = attr ? attr->guardsize : __getpagesize (); guardaddr = mmap ((caddr_t)((char *)(new_thread+1) - STACK_SIZE), - attr ? attr->guardsize : __getpagesize (), - 0, MAP_FIXED, -1, 0); + guardsize, 0, MAP_FIXED, -1, 0); if (guardaddr == MAP_FAILED) - /* We don't make this an error. */ - guardaddr = NULL; + { + /* We don't make this an error. */ + guardaddr = NULL; + guardsize = 0; + } } break; } @@ -238,7 +272,7 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, new_thread->p_nextwaiting = NULL; new_thread->p_tid = new_thread_id; new_thread->p_priority = 0; - new_thread->p_spinlock = &(__pthread_handles[sseg].h_spinlock); + new_thread->p_lock = &(__pthread_handles[sseg].h_lock); new_thread->p_signal = 0; new_thread->p_signal_jmp = NULL; new_thread->p_cancel_jmp = NULL; @@ -255,16 +289,15 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, new_thread->p_errno = 0; new_thread->p_h_errnop = &new_thread->p_h_errno; new_thread->p_h_errno = 0; + new_thread->p_in_sighandler = NULL; + new_thread->p_sigwaiting = 0; new_thread->p_guardaddr = guardaddr; - new_thread->p_guardsize = (guardaddr == NULL - ? 0 - : (attr == NULL - ? __getpagesize () : attr->guardsize)); + new_thread->p_guardsize = guardsize; new_thread->p_userstack = attr != NULL && attr->stackaddr_set; memset (new_thread->p_specific, '\0', PTHREAD_KEY_1STLEVEL_SIZE * sizeof (new_thread->p_specific[0])); /* Initialize the thread handle */ - __pthread_handles[sseg].h_spinlock = 0; /* should already be 0 */ + __pthread_init_lock(&__pthread_handles[sseg].h_lock); __pthread_handles[sseg].h_descr = new_thread; __pthread_handles[sseg].h_bottom = new_thread_bottom; /* Determine scheduling parameters for the thread */ @@ -305,6 +338,8 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, munmap(new_thread->p_guardaddr, new_thread->p_guardsize); } __pthread_handles[sseg].h_descr = NULL; + __pthread_handles[sseg].h_bottom = NULL; + __pthread_handles_num--; return errno; } /* Insert new thread in doubly linked list of active threads */ @@ -330,10 +365,12 @@ static void pthread_free(pthread_descr th) ASSERT(th->p_exited); /* Make the handle invalid */ handle = thread_handle(th->p_tid); - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); handle->h_descr = NULL; handle->h_bottom = (char *)(-1L); - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); + /* One fewer threads in __pthread_handles */ + __pthread_handles_num--; /* If initial thread, nothing to free */ if (th == &__pthread_initial_thread) return; if (!th->p_userstack) @@ -360,10 +397,10 @@ static void pthread_exited(pid_t pid) th->p_nextlive->p_prevlive = th->p_prevlive; th->p_prevlive->p_nextlive = th->p_nextlive; /* Mark thread as exited, and if detached, free its resources */ - acquire(th->p_spinlock); + __pthread_lock(th->p_lock); th->p_exited = 1; detached = th->p_detached; - release(th->p_spinlock); + __pthread_unlock(th->p_lock); if (detached) pthread_free(th); break; } @@ -409,16 +446,16 @@ static void pthread_handle_free(pthread_descr th) } while (t != __pthread_main_thread); if (t != th) return; - acquire(th->p_spinlock); + __pthread_lock(th->p_lock); if (th->p_exited) { - release(th->p_spinlock); + __pthread_unlock(th->p_lock); pthread_free(th); } else { /* The Unix process of the thread is still running. Mark the thread as detached so that the thread manager will deallocate its resources when the Unix process exits. */ th->p_detached = 1; - release(th->p_spinlock); + __pthread_unlock(th->p_lock); } } diff --git a/linuxthreads/mutex.c b/linuxthreads/mutex.c index 3b40ac04c5..54504bfbc3 100644 --- a/linuxthreads/mutex.c +++ b/linuxthreads/mutex.c @@ -26,23 +26,18 @@ int __pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * mutex_attr) { - mutex->m_spinlock = 0; - mutex->m_count = 0; - mutex->m_owner = NULL; + __pthread_init_lock(&mutex->m_lock); mutex->m_kind = mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->mutexkind; - queue_init(&mutex->m_waiting); + mutex->m_count = 0; + mutex->m_owner = NULL; return 0; } weak_alias (__pthread_mutex_init, pthread_mutex_init) int __pthread_mutex_destroy(pthread_mutex_t * mutex) { - int count; - acquire(&mutex->m_spinlock); - count = mutex->m_count; - release(&mutex->m_spinlock); - if (count > 0) return EBUSY; + if (mutex->m_lock.status != 0) return EBUSY; return 0; } weak_alias (__pthread_mutex_destroy, pthread_mutex_destroy) @@ -50,40 +45,33 @@ weak_alias (__pthread_mutex_destroy, pthread_mutex_destroy) int __pthread_mutex_trylock(pthread_mutex_t * mutex) { pthread_descr self; + int retcode; - acquire(&mutex->m_spinlock); switch(mutex->m_kind) { case PTHREAD_MUTEX_FAST_NP: - if (mutex->m_count == 0) { - mutex->m_count = 1; - release(&mutex->m_spinlock); - return 0; - } - break; + retcode = __pthread_trylock(&mutex->m_lock); + return retcode; case PTHREAD_MUTEX_RECURSIVE_NP: self = thread_self(); - if (mutex->m_count == 0 || mutex->m_owner == self) { + if (mutex->m_owner == self) { mutex->m_count++; - mutex->m_owner = self; - release(&mutex->m_spinlock); return 0; } - break; + retcode = __pthread_trylock(&mutex->m_lock); + if (retcode == 0) { + mutex->m_owner = self; + mutex->m_count = 0; + } + return retcode; case PTHREAD_MUTEX_ERRORCHECK_NP: - self = thread_self(); - if (mutex->m_count == 0) { - mutex->m_count = 1; - mutex->m_owner = self; - release(&mutex->m_spinlock); - return 0; + retcode = __pthread_trylock(&mutex->m_lock); + if (retcode == 0) { + mutex->m_owner = thread_self(); } - break; + return retcode; default: - release(&mutex->m_spinlock); return EINVAL; } - release(&mutex->m_spinlock); - return EBUSY; } weak_alias (__pthread_mutex_trylock, pthread_mutex_trylock) @@ -91,87 +79,55 @@ int __pthread_mutex_lock(pthread_mutex_t * mutex) { pthread_descr self; - acquire(&mutex->m_spinlock); switch(mutex->m_kind) { case PTHREAD_MUTEX_FAST_NP: - if (mutex->m_count == 0) { - mutex->m_count = 1; - release(&mutex->m_spinlock); - return 0; - } - self = thread_self(); - break; + __pthread_lock(&mutex->m_lock); + return 0; case PTHREAD_MUTEX_RECURSIVE_NP: self = thread_self(); - if (mutex->m_count == 0 || mutex->m_owner == self) { + if (mutex->m_owner == self) { mutex->m_count++; - mutex->m_owner = self; - release(&mutex->m_spinlock); return 0; } - break; + __pthread_lock(&mutex->m_lock); + mutex->m_owner = self; + mutex->m_count = 0; + return 0; case PTHREAD_MUTEX_ERRORCHECK_NP: self = thread_self(); - if (mutex->m_count == 0) { - mutex->m_count = 1; - mutex->m_owner = self; - release(&mutex->m_spinlock); - return 0; - } else if (mutex->m_owner == self) { - release(&mutex->m_spinlock); - return EDEADLK; - } - break; + if (mutex->m_owner == self) return EDEADLK; + __pthread_lock(&mutex->m_lock); + mutex->m_owner = self; + return 0; default: - release(&mutex->m_spinlock); return EINVAL; } - /* Suspend ourselves */ - enqueue(&mutex->m_waiting, self); - release(&mutex->m_spinlock); - suspend(self); /* This is not a cancellation point */ - /* Now we own the mutex */ - ASSERT(mutex->m_count == 1); - mutex->m_owner = self; /* for recursive and errorcheck mutexes */ - return 0; } weak_alias (__pthread_mutex_lock, pthread_mutex_lock) int __pthread_mutex_unlock(pthread_mutex_t * mutex) { - pthread_descr th; - - acquire(&mutex->m_spinlock); switch (mutex->m_kind) { case PTHREAD_MUTEX_FAST_NP: - break; + __pthread_unlock(&mutex->m_lock); + return 0; case PTHREAD_MUTEX_RECURSIVE_NP: - if (mutex->m_count >= 2) { + if (mutex->m_count > 0) { mutex->m_count--; - release(&mutex->m_spinlock); return 0; } - break; + mutex->m_owner = NULL; + __pthread_unlock(&mutex->m_lock); + return 0; case PTHREAD_MUTEX_ERRORCHECK_NP: - if (mutex->m_count == 0 || mutex->m_owner != thread_self()) { - release(&mutex->m_spinlock); + if (mutex->m_owner != thread_self() || mutex->m_lock.status == 0) return EPERM; - } - break; + mutex->m_owner = NULL; + __pthread_unlock(&mutex->m_lock); + return 0; default: - release(&mutex->m_spinlock); return EINVAL; } - th = dequeue(&mutex->m_waiting); - /* If no waiters, unlock the mutex */ - if (th == NULL) mutex->m_count = 0; - release(&mutex->m_spinlock); - /* If there is a waiter, restart it with the mutex still locked */ - if (th != NULL) { - mutex->m_owner = NULL; /* we no longer own the mutex */ - restart(th); - } - return 0; } weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock) diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c index 83d160622d..e95b352b72 100644 --- a/linuxthreads/pthread.c +++ b/linuxthreads/pthread.c @@ -35,7 +35,7 @@ struct _pthread_descr_struct __pthread_initial_thread = { PTHREAD_THREADS_MAX, /* pthread_t p_tid */ 0, /* int p_pid */ 0, /* int p_priority */ - &__pthread_handles[0].h_spinlock, /* int * p_spinlock */ + &__pthread_handles[0].h_lock, /* struct _pthread_fastlock * p_lock */ 0, /* int p_signal */ NULL, /* sigjmp_buf * p_signal_buf */ NULL, /* sigjmp_buf * p_cancel_buf */ @@ -53,6 +53,8 @@ struct _pthread_descr_struct __pthread_initial_thread = { 0, /* int p_errno */ NULL, /* int *p_h_errnop */ 0, /* int p_h_errno */ + NULL, /* char * p_in_sighandler */ + 0, /* char p_sigwaiting */ PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */ {NULL} /* void * p_specific[PTHREAD_KEYS_MAX] */ }; @@ -68,7 +70,7 @@ struct _pthread_descr_struct __pthread_manager_thread = { 0, /* int p_tid */ 0, /* int p_pid */ 0, /* int p_priority */ - NULL, /* int * p_spinlock */ + NULL, /* struct _pthread_fastlock * p_lock */ 0, /* int p_signal */ NULL, /* sigjmp_buf * p_signal_buf */ NULL, /* sigjmp_buf * p_cancel_buf */ @@ -86,6 +88,8 @@ struct _pthread_descr_struct __pthread_manager_thread = { 0, /* int p_errno */ NULL, /* int *p_h_errnop */ 0, /* int p_h_errno */ + NULL, /* char * p_in_sighandler */ + 0, /* char p_sigwaiting */ PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */ {NULL} /* void * p_specific[PTHREAD_KEYS_MAX] */ }; @@ -119,6 +123,15 @@ char *__pthread_manager_thread_tos = NULL; int __pthread_exit_requested = 0; int __pthread_exit_code = 0; +/* Communicate relevant LinuxThreads constants to gdb */ + +const int __pthread_threads_max = PTHREAD_THREADS_MAX; +const int __pthread_sizeof_handle = sizeof(struct pthread_handle_struct); +const int __pthread_offsetof_descr = offsetof(struct pthread_handle_struct, + h_descr); +const int __pthread_offsetof_pid = offsetof(struct _pthread_descr_struct, + p_pid); + /* Signal numbers used for the communication. */ int __pthread_sig_restart; int __pthread_sig_cancel; @@ -131,6 +144,7 @@ extern int _h_errno; static void pthread_exit_process(int retcode, void *arg); static void pthread_handle_sigcancel(int sig); +static void pthread_handle_sigrestart(int sig); /* Initialize the pthread library. Initialization is split in two functions: @@ -148,6 +162,10 @@ static void pthread_initialize(void) /* If already done (e.g. by a constructor called earlier!), bail out */ if (__pthread_initial_thread_bos != NULL) return; +#ifdef TEST_FOR_COMPARE_AND_SWAP + /* Test if compare-and-swap is available */ + __pthread_has_cas = compare_and_swap_is_available(); +#endif /* For the initial stack, reserve at least STACK_SIZE bytes of stack below the current stack address, and align that on a STACK_SIZE boundary. */ @@ -178,14 +196,14 @@ static void pthread_initialize(void) /* Setup signal handlers for the initial thread. Since signal handlers are shared between threads, these settings will be inherited by all other threads. */ - sa.sa_handler = __pthread_sighandler; + sa.sa_handler = pthread_handle_sigrestart; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; /* does not matter for regular threads, but better for the thread manager */ - sigaction(PTHREAD_SIG_RESTART, &sa, NULL); + __sigaction(PTHREAD_SIG_RESTART, &sa, NULL); sa.sa_handler = pthread_handle_sigcancel; sa.sa_flags = 0; - sigaction(PTHREAD_SIG_CANCEL, &sa, NULL); + __sigaction(PTHREAD_SIG_CANCEL, &sa, NULL); /* Initially, block PTHREAD_SIG_RESTART. Will be unblocked on demand. */ sigemptyset(&mask); @@ -197,10 +215,11 @@ static void pthread_initialize(void) __on_exit(pthread_exit_process, NULL); } -static int pthread_initialize_manager(void) +int __pthread_initialize_manager(void) { int manager_pipe[2]; int pid; + struct pthread_request request; /* If basic initialization not done yet (e.g. we're called from a constructor run before our constructor), do it now */ @@ -228,6 +247,11 @@ static int pthread_initialize_manager(void) __pthread_manager_request = manager_pipe[1]; /* writing end */ __pthread_manager_reader = manager_pipe[0]; /* reading end */ __pthread_manager_thread.p_pid = pid; + /* Make gdb aware of new thread manager */ + if (__pthread_threads_debug) raise(PTHREAD_SIG_CANCEL); + /* Synchronize debugging of the thread manager */ + request.req_kind = REQ_DEBUG; + __libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); return 0; } @@ -239,7 +263,7 @@ int __pthread_create_2_1(pthread_t *thread, const pthread_attr_t *attr, pthread_descr self = thread_self(); struct pthread_request request; if (__pthread_manager_request < 0) { - if (pthread_initialize_manager() < 0) return EAGAIN; + if (__pthread_initialize_manager() < 0) return EAGAIN; } request.req_thread = self; request.req_kind = REQ_CREATE; @@ -296,6 +320,24 @@ int pthread_equal(pthread_t thread1, pthread_t thread2) return thread1 == thread2; } +/* Helper function for thread_self in the case of user-provided stacks */ + +#ifndef THREAD_SELF + +pthread_descr __pthread_find_self() +{ + char * sp = CURRENT_STACK_FRAME; + pthread_handle h; + + /* __pthread_handles[0] is the initial thread, handled specially in + thread_self(), so start at 1 */ + h = __pthread_handles + 1; + while (! (sp <= (char *) h->h_descr && sp >= h->h_bottom)) h++; + return h->h_descr; +} + +#endif + /* Thread scheduling */ int pthread_setschedparam(pthread_t thread, int policy, @@ -304,18 +346,18 @@ int pthread_setschedparam(pthread_t thread, int policy, pthread_handle handle = thread_handle(thread); pthread_descr th; - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); if (invalid_handle(handle, thread)) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return ESRCH; } th = handle->h_descr; if (__sched_setscheduler(th->p_pid, policy, param) == -1) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return errno; } th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); if (__pthread_manager_request >= 0) __pthread_manager_adjust_prio(th->p_priority); return 0; @@ -327,13 +369,13 @@ int pthread_getschedparam(pthread_t thread, int *policy, pthread_handle handle = thread_handle(thread); int pid, pol; - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); if (invalid_handle(handle, thread)) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return ESRCH; } pid = handle->h_descr->p_pid; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); pol = __sched_getscheduler(pid); if (pol == -1) return errno; if (__sched_getparam(pid, param) == -1) return errno; @@ -364,11 +406,11 @@ static void pthread_exit_process(int retcode, void *arg) /* The handler for the RESTART signal just records the signal received in the thread descriptor, and optionally performs a siglongjmp - (for pthread_cond_timedwait). Also used in sigwait. + (for pthread_cond_timedwait). For the thread manager thread, redirect the signal to __pthread_manager_sighandler. */ -void __pthread_sighandler(int sig) +static void pthread_handle_sigrestart(int sig) { pthread_descr self = thread_self(); if (self == &__pthread_manager_thread) { @@ -380,13 +422,24 @@ void __pthread_sighandler(int sig) } /* The handler for the CANCEL signal checks for cancellation - (in asynchronous mode) and for process-wide exit and exec requests. */ + (in asynchronous mode), for process-wide exit and exec requests. + For the thread manager thread, we ignore the signal. + The debugging strategy is as follows: + On reception of a REQ_DEBUG request (sent by new threads created to + the thread manager under debugging mode), the thread manager throws + PTHREAD_SIG_CANCEL to itself. The debugger (if active) intercepts + this signal, takes into account new threads and continue execution + of the thread manager by propagating the signal because it doesn't + know what it is specifically done for. In the current implementation, + the thread manager simply discards it. */ static void pthread_handle_sigcancel(int sig) { pthread_descr self = thread_self(); sigjmp_buf * jmpbuf; + if (self == &__pthread_manager_thread) + return; if (__pthread_exit_requested) { /* Main thread should accumulate times for thread manager and its children, so that timings for main thread account for all threads. */ @@ -469,7 +522,7 @@ weak_alias (__pthread_getconcurrency, pthread_getconcurrency) #ifdef DEBUG #include -void __pthread_message(char * fmt, long arg) +void __pthread_message(char * fmt, ...) { char buffer[1024]; va_list args; diff --git a/linuxthreads/ptlongjmp.c b/linuxthreads/ptlongjmp.c index c5704f9e9a..580ae93f88 100644 --- a/linuxthreads/ptlongjmp.c +++ b/linuxthreads/ptlongjmp.c @@ -37,6 +37,8 @@ static void pthread_cleanup_upto(__jmp_buf target) c = c->prev) c->routine(c->arg); self->p_cleanup = c; + if (self->p_in_sighandler && _JMPBUF_UNWINDS(target, self->p_in_sighandler)) + self->p_in_sighandler = NULL; } void siglongjmp(sigjmp_buf env, int val) diff --git a/linuxthreads/queue.h b/linuxthreads/queue.h index 60039cce6e..fa8c5d861d 100644 --- a/linuxthreads/queue.h +++ b/linuxthreads/queue.h @@ -14,49 +14,42 @@ /* Waiting queues */ -typedef struct _pthread_queue pthread_queue; +/* Waiting queues are represented by lists of thread descriptors + linked through their p_nextwaiting field. The lists are kept + sorted by decreasing priority, and then decreasing waiting time. */ -static inline void queue_init(pthread_queue * q) +static inline void enqueue(pthread_descr * q, pthread_descr th) { - q->head = q->tail = NULL; -} - -static inline void enqueue(pthread_queue * q, pthread_descr th) -{ - int prio; - pthread_descr * elt; - + int prio = th->p_priority; ASSERT(th->p_nextwaiting == NULL); - if (q->tail == NULL) { - q->head = th; - q->tail = th; - return; - } - prio = th->p_priority; - if (prio > 0) { - /* Insert in queue according to priority order */ - for (elt = &(q->head); *elt != NULL; elt = &((*elt)->p_nextwaiting)) { - if (prio > (*elt)->p_priority) { - th->p_nextwaiting = *elt; - *elt = th; - return; - } + for (; *q != NULL; q = &((*q)->p_nextwaiting)) { + if (prio > (*q)->p_priority) { + th->p_nextwaiting = *q; + *q = th; + return; } } - /* Priority is no greater than any thread in the queue. - Insert at end of queue */ - q->tail->p_nextwaiting = th; - q->tail = th; + *q = th; } -static inline pthread_descr dequeue(pthread_queue * q) +static inline pthread_descr dequeue(pthread_descr * q) { pthread_descr th; - th = q->head; + th = *q; if (th != NULL) { - q->head = th->p_nextwaiting; - if (q->head == NULL) q->tail = NULL; + *q = th->p_nextwaiting; th->p_nextwaiting = NULL; } return th; } + +static inline void remove_from_queue(pthread_descr * q, pthread_descr th) +{ + for (; *q != NULL; q = &((*q)->p_nextwaiting)) { + if (*q == th) { + *q = th->p_nextwaiting; + th->p_nextwaiting = NULL; + return; + } + } +} diff --git a/linuxthreads/rwlock.c b/linuxthreads/rwlock.c index c6b281551a..99209f75da 100644 --- a/linuxthreads/rwlock.c +++ b/linuxthreads/rwlock.c @@ -30,12 +30,11 @@ int pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { - rwlock->rw_spinlock = 0; + __pthread_init_lock(&rwlock->rw_lock); rwlock->rw_readers = 0; rwlock->rw_writer = NULL; - - queue_init(&rwlock->rw_read_waiting); - queue_init(&rwlock->rw_write_waiting); + rwlock->rw_read_waiting = NULL; + rwlock->rw_write_waiting = NULL; if (attr == NULL) { @@ -58,10 +57,10 @@ pthread_rwlock_destroy (pthread_rwlock_t *rwlock) int readers; _pthread_descr writer; - acquire (&rwlock->rw_spinlock); + __pthread_lock (&rwlock->rw_lock); readers = rwlock->rw_readers; writer = rwlock->rw_writer; - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); if (readers > 0 || writer != NULL) return EBUSY; @@ -77,7 +76,7 @@ pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) while (1) { - acquire (&rwlock->rw_spinlock); + __pthread_lock (&rwlock->rw_lock); if (rwlock->rw_writer == NULL || (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP && rwlock->rw_readers != 0)) @@ -87,12 +86,12 @@ pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) /* Suspend ourselves, then try again */ self = thread_self (); enqueue (&rwlock->rw_read_waiting, self); - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); suspend (self); /* This is not a cancellation point */ } ++rwlock->rw_readers; - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); return 0; } @@ -103,7 +102,7 @@ pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) { int result = EBUSY; - acquire (&rwlock->rw_spinlock); + __pthread_lock (&rwlock->rw_lock); if (rwlock->rw_writer == NULL || (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP && rwlock->rw_readers != 0)) @@ -111,7 +110,7 @@ pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) ++rwlock->rw_readers; result = 0; } - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); return result; } @@ -124,17 +123,17 @@ pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) while(1) { - acquire (&rwlock->rw_spinlock); + __pthread_lock (&rwlock->rw_lock); if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL) { rwlock->rw_writer = self; - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); return 0; } /* Suspend ourselves, then try again */ enqueue (&rwlock->rw_write_waiting, self); - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); suspend (self); /* This is not a cancellation point */ } } @@ -145,13 +144,13 @@ pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) { int result = EBUSY; - acquire (&rwlock->rw_spinlock); + __pthread_lock (&rwlock->rw_lock); if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL) { rwlock->rw_writer = thread_self (); result = 0; } - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); return result; } @@ -160,16 +159,16 @@ pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) int pthread_rwlock_unlock (pthread_rwlock_t *rwlock) { - struct _pthread_queue torestart; + pthread_descr torestart; pthread_descr th; - acquire (&rwlock->rw_spinlock); + __pthread_lock (&rwlock->rw_lock); if (rwlock->rw_writer != NULL) { /* Unlocking a write lock. */ if (rwlock->rw_writer != thread_self ()) { - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); return EPERM; } rwlock->rw_writer = NULL; @@ -179,15 +178,15 @@ pthread_rwlock_unlock (pthread_rwlock_t *rwlock) { /* Restart all waiting readers. */ torestart = rwlock->rw_read_waiting; - queue_init (&rwlock->rw_read_waiting); - release (&rwlock->rw_spinlock); + rwlock->rw_read_waiting = NULL; + __pthread_unlock (&rwlock->rw_lock); while ((th = dequeue (&torestart)) != NULL) restart (th); } else { /* Restart one waiting writer. */ - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); restart (th); } } @@ -196,7 +195,7 @@ pthread_rwlock_unlock (pthread_rwlock_t *rwlock) /* Unlocking a read lock. */ if (rwlock->rw_readers == 0) { - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); return EPERM; } @@ -207,7 +206,7 @@ pthread_rwlock_unlock (pthread_rwlock_t *rwlock) else th = NULL; - release (&rwlock->rw_spinlock); + __pthread_unlock (&rwlock->rw_lock); if (th != NULL) restart (th); } diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c index bd07439cde..7ca609f6b2 100644 --- a/linuxthreads/semaphore.c +++ b/linuxthreads/semaphore.c @@ -17,69 +17,13 @@ #include "pthread.h" #include "semaphore.h" #include "internals.h" +#include "spinlock.h" #include "restart.h" - - -#if !defined HAS_COMPARE_AND_SWAP && !defined TEST_FOR_COMPARE_AND_SWAP -/* If we have no atomic compare and swap, fake it using an extra spinlock. */ - -#include "spinlock.h" -static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval) -{ - int ret; - acquire(&sem->sem_spinlock); - ret = (sem->sem_status == oldval); - if (ret) sem->sem_status = newval; - release(&sem->sem_spinlock); - return ret; -} - -#elif defined TEST_FOR_COMPARE_AND_SWAP - -#include "spinlock.h" -static int has_compare_and_swap = -1; /* to be determined at run-time */ - -static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval) -{ - int ret; - - if (has_compare_and_swap == 1) - return __compare_and_swap(&sem->sem_status, oldval, newval); - - acquire(&sem->sem_spinlock); - ret = (sem->sem_status == oldval); - if (ret) sem->sem_status = newval; - release(&sem->sem_spinlock); - return ret; -} - -#else -/* But if we do have an atomic compare and swap, use it! */ - -static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval) -{ - return __compare_and_swap(&sem->sem_status, oldval, newval); -} - -#endif - - -/* The state of a semaphore is represented by a long int encoding - either the semaphore count if >= 0 and no thread is waiting on it, - or the head of the list of threads waiting for the semaphore. - To distinguish the two cases, we encode the semaphore count N - as 2N+1, so that it has the lowest bit set. - - A sequence of sem_wait operations on a semaphore initialized to N - result in the following successive states: - 2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ... -*/ - -static void sem_restart_list(pthread_descr waiting); +#include "queue.h" int sem_init(sem_t *sem, int pshared, unsigned int value) { - if ((long)value > SEM_VALUE_MAX) { + if (value > SEM_VALUE_MAX) { errno = EINVAL; return -1; } @@ -87,150 +31,104 @@ int sem_init(sem_t *sem, int pshared, unsigned int value) errno = ENOSYS; return -1; } -#ifdef TEST_FOR_COMPARE_AND_SWAP - if (has_compare_and_swap == -1) { - has_compare_and_swap = compare_and_swap_is_available(); - } -#endif -#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP - sem->sem_spinlock = 0; -#endif - sem->sem_status = ((long)value << 1) + 1; + __pthread_init_lock((struct _pthread_fastlock *) &sem->sem_lock); + sem->sem_value = value; + sem->sem_waiting = NULL; return 0; } int sem_wait(sem_t * sem) { - long oldstatus, newstatus; - volatile pthread_descr self = thread_self(); - pthread_descr * th; + volatile pthread_descr self; - while (1) { - do { - oldstatus = sem->sem_status; - if ((oldstatus & 1) && (oldstatus != 1)) - newstatus = oldstatus - 2; - else { - newstatus = (long) self; - self->p_nextwaiting = (pthread_descr) oldstatus; - } - } - while (! sem_compare_and_swap(sem, oldstatus, newstatus)); - if (newstatus & 1) - /* We got the semaphore. */ - return 0; - /* Wait for sem_post or cancellation */ - suspend_with_cancellation(self); - /* This is a cancellation point */ - if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { - /* Remove ourselves from the waiting list if we're still on it */ - /* First check if we're at the head of the list. */ - do { - oldstatus = sem->sem_status; - if (oldstatus != (long) self) break; - newstatus = (long) self->p_nextwaiting; - } - while (! sem_compare_and_swap(sem, oldstatus, newstatus)); - /* Now, check if we're somewhere in the list. - There's a race condition with sem_post here, but it does not matter: - the net result is that at the time pthread_exit is called, - self is no longer reachable from sem->sem_status. */ - if (oldstatus != (long) self && (oldstatus & 1) == 0) { - for (th = &(((pthread_descr) oldstatus)->p_nextwaiting); - *th != NULL && *th != (pthread_descr) 1; - th = &((*th)->p_nextwaiting)) { - if (*th == self) { - *th = self->p_nextwaiting; - break; - } - } - } - pthread_exit(PTHREAD_CANCELED); - } + __pthread_lock((struct _pthread_fastlock *) &sem->sem_lock); + if (sem->sem_value > 0) { + sem->sem_value--; + __pthread_unlock((struct _pthread_fastlock *) &sem->sem_lock); + return 0; } + self = thread_self(); + enqueue(&sem->sem_waiting, self); + /* Wait for sem_post or cancellation */ + __pthread_unlock((struct _pthread_fastlock *) &sem->sem_lock); + suspend_with_cancellation(self); + /* This is a cancellation point */ + if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { + /* Remove ourselves from the waiting list if we're still on it */ + __pthread_lock((struct _pthread_fastlock *) &sem->sem_lock); + remove_from_queue(&sem->sem_waiting, self); + __pthread_unlock((struct _pthread_fastlock *) &sem->sem_lock); + pthread_exit(PTHREAD_CANCELED); + } + /* We got the semaphore */ + return 0; } int sem_trywait(sem_t * sem) { - long oldstatus, newstatus; + int retval; - do { - oldstatus = sem->sem_status; - if ((oldstatus & 1) == 0 || (oldstatus == 1)) { - errno = EAGAIN; - return -1; - } - newstatus = oldstatus - 2; + __pthread_lock((struct _pthread_fastlock *) &sem->sem_lock); + if (sem->sem_value == 0) { + errno = EAGAIN; + retval = -1; + } else { + sem->sem_value--; + retval = 0; } - while (! sem_compare_and_swap(sem, oldstatus, newstatus)); - return 0; + return retval; } int sem_post(sem_t * sem) { - long oldstatus, newstatus; + pthread_descr self = thread_self(); + pthread_descr th; + struct pthread_request request; - do { - oldstatus = sem->sem_status; - if ((oldstatus & 1) == 0) - newstatus = 3; - else { - if (oldstatus >= SEM_VALUE_MAX) { + if (self->p_in_sighandler == NULL) { + __pthread_lock((struct _pthread_fastlock *) &sem->sem_lock); + if (sem->sem_waiting == NULL) { + if (sem->sem_value >= SEM_VALUE_MAX) { /* Overflow */ errno = ERANGE; + __pthread_unlock((struct _pthread_fastlock *) &sem->sem_lock); return -1; } - newstatus = oldstatus + 2; + sem->sem_value++; + __pthread_unlock((struct _pthread_fastlock *) &sem->sem_lock); + } else { + th = dequeue(&sem->sem_waiting); + __pthread_unlock((struct _pthread_fastlock *) &sem->sem_lock); + restart(th); } + } else { + /* If we're in signal handler, delegate post operation to + the thread manager. */ + if (__pthread_manager_request < 0) { + if (__pthread_initialize_manager() < 0) { + errno = EAGAIN; + return -1; + } + } + request.req_kind = REQ_POST; + request.req_args.post = sem; + __libc_write(__pthread_manager_request, + (char *) &request, sizeof(request)); } - while (! sem_compare_and_swap(sem, oldstatus, newstatus)); - if ((oldstatus & 1) == 0) - sem_restart_list((pthread_descr) oldstatus); return 0; } int sem_getvalue(sem_t * sem, int * sval) { - long status = sem->sem_status; - if (status & 1) - *sval = (int)((unsigned long) status >> 1); - else - *sval = 0; + *sval = sem->sem_value; return 0; } int sem_destroy(sem_t * sem) { - if ((sem->sem_status & 1) == 0) { + if (sem->sem_waiting != NULL) { errno = EBUSY; return -1; } return 0; } - -/* Auxiliary function for restarting all threads on a waiting list, - in priority order. */ - -static void sem_restart_list(pthread_descr waiting) -{ - pthread_descr th, towake, *p; - - /* Sort list of waiting threads by decreasing priority (insertion sort) */ - towake = NULL; - while (waiting != (pthread_descr) 1) { - th = waiting; - waiting = waiting->p_nextwaiting; - p = &towake; - while (*p != NULL && th->p_priority < (*p)->p_priority) - p = &((*p)->p_nextwaiting); - th->p_nextwaiting = *p; - *p = th; - } - /* Wake up threads in priority order */ - while (towake != NULL) { - th = towake; - towake = towake->p_nextwaiting; - th->p_nextwaiting = NULL; - restart(th); - } -} diff --git a/linuxthreads/semaphore.h b/linuxthreads/semaphore.h index 9f01a7f4fa..0740962902 100644 --- a/linuxthreads/semaphore.h +++ b/linuxthreads/semaphore.h @@ -21,8 +21,11 @@ #define SEM_VALUE_MAX INT_MAX -/* Get the semaphore structure definition. */ -#include +typedef struct { + struct { long status; int spinlock; } sem_lock; + int sem_value; + _pthread_descr sem_waiting; +} sem_t; __BEGIN_DECLS diff --git a/linuxthreads/signals.c b/linuxthreads/signals.c index 905e11e5fe..4a83b14d83 100644 --- a/linuxthreads/signals.c +++ b/linuxthreads/signals.c @@ -53,83 +53,88 @@ int pthread_kill(pthread_t thread, int signo) pthread_handle handle = thread_handle(thread); int pid; - acquire(&handle->h_spinlock); + __pthread_lock(&handle->h_lock); if (invalid_handle(handle, thread)) { - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); return ESRCH; } pid = handle->h_descr->p_pid; - release(&handle->h_spinlock); + __pthread_unlock(&handle->h_lock); if (kill(pid, signo) == -1) return errno; else return 0; } -/* The set of signals on which some thread is doing a sigwait */ -static sigset_t sigwaited; -static pthread_mutex_t sigwaited_mut = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t sigwaited_changed = PTHREAD_COND_INITIALIZER; +/* User-provided signal handlers */ +static __sighandler_t sighandler[NSIG]; + +/* The wrapper around user-provided signal handlers */ +static void pthread_sighandler(int signo) +{ + pthread_descr self = thread_self(); + char * in_sighandler; + /* If we're in a sigwait operation, just record the signal received + and return without calling the user's handler */ + if (self->p_sigwaiting) { + self->p_sigwaiting = 0; + self->p_signal = signo; + return; + } + /* Record that we're in a signal handler and call the user's + handler function */ + in_sighandler = self->p_in_sighandler; + if (in_sighandler == NULL) self->p_in_sighandler = CURRENT_STACK_FRAME; + sighandler[signo](signo); + if (in_sighandler == NULL) self->p_in_sighandler = NULL; +} + +int sigaction(int sig, const struct sigaction * act, + struct sigaction * oact) +{ + struct sigaction newact; + + if (sig == PTHREAD_SIG_RESTART || sig == PTHREAD_SIG_CANCEL) + return EINVAL; + newact = *act; + if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) + newact.sa_handler = pthread_sighandler; + if (__sigaction(sig, &newact, oact) == -1) + return -1; + if (oact != NULL) oact->sa_handler = sighandler[sig]; + sighandler[sig] = act->sa_handler; + return 0; +} int sigwait(const sigset_t * set, int * sig) { volatile pthread_descr self = thread_self(); sigset_t mask; int s; - struct sigaction action, saved_signals[NSIG]; sigjmp_buf jmpbuf; - pthread_mutex_lock(&sigwaited_mut); - /* Make sure no other thread is waiting on our signals */ -test_again: - for (s = 1; s < NSIG; s++) { - if (sigismember(set, s) && sigismember(&sigwaited, s)) { - pthread_cond_wait(&sigwaited_changed, &sigwaited_mut); - goto test_again; - } - } /* Get ready to block all signals except those in set and the cancellation signal */ sigfillset(&mask); sigdelset(&mask, PTHREAD_SIG_CANCEL); - /* Signals in set are assumed blocked on entrance */ - /* Install our signal handler on all signals in set, - and unblock them in mask. - Also mark those signals as being sigwaited on */ - for (s = 1; s < NSIG; s++) { - if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) { + for (s = 1; s <= NSIG; s++) { + if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) sigdelset(&mask, s); - action.sa_handler = __pthread_sighandler; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - sigaction(s, &action, &(saved_signals[s])); - sigaddset(&sigwaited, s); - } } - pthread_mutex_unlock(&sigwaited_mut); - /* Test for cancellation */ if (sigsetjmp(jmpbuf, 1) == 0) { self->p_cancel_jmp = &jmpbuf; if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) { /* Reset the signal count */ self->p_signal = 0; + /* Say we're in sigwait */ + self->p_sigwaiting = 1; /* Unblock the signals and wait for them */ sigsuspend(&mask); } } self->p_cancel_jmp = NULL; - /* The signals are now reblocked. Restore the sighandlers. */ - pthread_mutex_lock(&sigwaited_mut); - for (s = 1; s < NSIG; s++) { - if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) { - sigaction(s, &(saved_signals[s]), NULL); - sigdelset(&sigwaited, s); - } - } - pthread_cond_broadcast(&sigwaited_changed); - pthread_mutex_unlock(&sigwaited_mut); - /* Check for cancellation */ + /* The signals are now reblocked. Check for cancellation */ pthread_testcancel(); /* We should have self->p_signal != 0 and equal to the signal received */ *sig = self->p_signal; diff --git a/linuxthreads/spinlock.c b/linuxthreads/spinlock.c index 170d9ae8d0..dba5d38fd2 100644 --- a/linuxthreads/spinlock.c +++ b/linuxthreads/spinlock.c @@ -12,15 +12,130 @@ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU Library General Public License for more details. */ -/* Spin locks */ +/* Internal locks */ #include #include #include "pthread.h" #include "internals.h" #include "spinlock.h" +#include "restart.h" -/* This function is called if the inlined test-and-set in acquire() failed */ +/* The status field of a fastlock has the following meaning: + 0: fastlock is free + 1: fastlock is taken, no thread is waiting on it + ADDR: fastlock is taken, ADDR is address of thread descriptor for + first waiting thread, other waiting threads are linked via + their p_nextwaiting field. + The waiting list is not sorted by priority order. + Actually, we always insert at top of list (sole insertion mode + that can be performed without locking). + For __pthread_unlock, we perform a linear search in the list + to find the highest-priority, oldest waiting thread. + This is safe because there are no concurrent __pthread_unlock + operations -- only the thread that locked the mutex can unlock it. */ + +void __pthread_lock(struct _pthread_fastlock * lock) +{ + long oldstatus, newstatus; + pthread_descr self = NULL; + + do { + oldstatus = lock->status; + if (oldstatus == 0) { + newstatus = 1; + } else { + self = thread_self(); + self->p_nextwaiting = (pthread_descr) oldstatus; + newstatus = (long) self; + } + } while(! compare_and_swap(&lock->status, oldstatus, newstatus, + &lock->spinlock)); + if (oldstatus != 0) suspend(self); +} + +int __pthread_trylock(struct _pthread_fastlock * lock) +{ + long oldstatus; + + do { + oldstatus = lock->status; + if (oldstatus != 0) return EBUSY; + } while(! compare_and_swap(&lock->status, 0, 1, &lock->spinlock)); + return 0; +} + +void __pthread_unlock(struct _pthread_fastlock * lock) +{ + long oldstatus; + pthread_descr thr, * ptr, * maxptr; + int maxprio; + +again: + oldstatus = lock->status; + if (oldstatus == 1) { + /* No threads are waiting for this lock */ + if (! compare_and_swap(&lock->status, 1, 0, &lock->spinlock)) goto again; + return; + } + /* Find thread in waiting queue with maximal priority */ + ptr = (pthread_descr *) &lock->status; + thr = (pthread_descr) oldstatus; + maxprio = 0; + maxptr = ptr; + while (thr != (pthread_descr) 1) { + if (thr->p_priority >= maxprio) { + maxptr = ptr; + maxprio = thr->p_priority; + } + ptr = &(thr->p_nextwaiting); + thr = *ptr; + } + /* Remove max prio thread from waiting list. */ + if (maxptr == (pthread_descr *) &lock->status) { + /* If max prio thread is at head, remove it with compare-and-swap + to guard against concurrent lock operation */ + thr = (pthread_descr) oldstatus; + if (! compare_and_swap(&lock->status, + oldstatus, (long)(thr->p_nextwaiting), + &lock->spinlock)) + goto again; + } else { + /* No risk of concurrent access, remove max prio thread normally */ + thr = *maxptr; + *maxptr = thr->p_nextwaiting; + } + /* Wake up the selected waiting thread */ + thr->p_nextwaiting = NULL; + restart(thr); +} + +/* Compare-and-swap emulation with a spinlock */ + +#ifdef TEST_FOR_COMPARE_AND_SWAP +int __pthread_has_cas = 0; +#endif + +#ifndef HAS_COMPARE_AND_SWAP + +static void __pthread_acquire(int * spinlock); + +int __pthread_compare_and_swap(long * ptr, long oldval, long newval, + int * spinlock) +{ + int res; + if (testandset(spinlock)) __pthread_acquire(spinlock); + if (*ptr == oldval) { + *ptr = newval; res = 1; + } else { + res = 0; + } + *spinlock = 0; + return res; +} + +/* This function is called if the inlined test-and-set + in __pthread_compare_and_swap() failed */ /* The retry strategy is as follows: - We test and set the spinlock MAX_SPIN_COUNT times, calling @@ -40,7 +155,7 @@ - When nanosleep() returns, we try again, doing MAX_SPIN_COUNT sched_yield(), then sleeping again if needed. */ -void __pthread_acquire(int * spinlock) +static void __pthread_acquire(int * spinlock) { int cnt = 0; struct timespec tm; @@ -57,3 +172,5 @@ void __pthread_acquire(int * spinlock) } } } + +#endif diff --git a/linuxthreads/spinlock.h b/linuxthreads/spinlock.h index 1707d3e42a..d21a967202 100644 --- a/linuxthreads/spinlock.h +++ b/linuxthreads/spinlock.h @@ -1,7 +1,6 @@ /* Linuxthreads - a simple clone()-based implementation of Posix */ /* threads for Linux. */ -/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) and */ -/* Richard Henderson (rth@tamu.edu) */ +/* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr) */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public License */ @@ -13,20 +12,52 @@ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU Library General Public License for more details. */ -/* Spin locks */ +/* Internal locks */ -extern void __pthread_acquire(int * spinlock); +extern void __pthread_lock(struct _pthread_fastlock * lock); +extern int __pthread_trylock(struct _pthread_fastlock * lock); +extern void __pthread_unlock(struct _pthread_fastlock * lock); -static inline void acquire(int * spinlock) +static inline void __pthread_init_lock(struct _pthread_fastlock * lock) { - if (testandset(spinlock)) __pthread_acquire(spinlock); + lock->status = 0; + lock->spinlock = 0; } -static inline void release(int * spinlock) +#define LOCK_INITIALIZER {0, 0} + +#if defined(TEST_FOR_COMPARE_AND_SWAP) + +extern int __pthread_has_cas; +extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval, + int * spinlock); + +static inline int compare_and_swap(long * ptr, long oldval, long newval, + int * spinlock) { -#ifndef RELEASE - *spinlock = 0; + if (__pthread_has_cas) + return __compare_and_swap(ptr, oldval, newval); + else + return __pthread_compare_and_swap(ptr, oldval, newval, spinlock); +} + +#elif defined(HAS_COMPARE_AND_SWAP) + +static inline int compare_and_swap(long * ptr, long oldval, long newval, + int * spinlock) +{ + return __compare_and_swap(ptr, oldval, newval); +} + #else - RELEASE(spinlock); -#endif + +extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval, + int * spinlock); + +static inline int compare_and_swap(long * ptr, long oldval, long newval, + int * spinlock) +{ + return __pthread_compare_and_swap(ptr, oldval, newval, spinlock); } + +#endif diff --git a/linuxthreads/sysdeps/i386/pt-machine.h b/linuxthreads/sysdeps/i386/pt-machine.h index ef4df2a1a3..c2a7d0883f 100644 --- a/linuxthreads/sysdeps/i386/pt-machine.h +++ b/linuxthreads/sysdeps/i386/pt-machine.h @@ -1,6 +1,6 @@ /* Machine-dependent pthreads configuration and inline functions. i386 version. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Richard Henderson . @@ -45,7 +45,9 @@ register char * stack_pointer __asm__ ("%esp"); We test dynamically whether it's available or not. */ #define HAS_COMPARE_AND_SWAP -#define TEST_FOR_COMPARE_AND_SWAP +#ifndef __i686__ +# define TEST_FOR_COMPARE_AND_SWAP +#endif extern inline int __compare_and_swap (long int *p, long int oldval, long int newval) diff --git a/linuxthreads/sysdeps/pthread/pthread.h b/linuxthreads/sysdeps/pthread/pthread.h index 9f5e9deef2..127e15deef 100644 --- a/linuxthreads/sysdeps/pthread/pthread.h +++ b/linuxthreads/sysdeps/pthread/pthread.h @@ -43,21 +43,24 @@ typedef unsigned long int pthread_t; /* Thread descriptors */ typedef struct _pthread_descr_struct *_pthread_descr; -/* Waiting queues (not abstract because mutexes and conditions aren't). */ -struct _pthread_queue +/* Fast locks (not abstract because mutexes and conditions aren't abstract). */ +struct _pthread_fastlock { - _pthread_descr head; /* First element, or NULL if queue empty. */ - _pthread_descr tail; /* Last element, or NULL if queue empty. */ + long status; /* "Free" or "taken" or head of waiting list */ + int spinlock; /* For compare-and-swap emulation */ }; /* Mutexes (not abstract because of PTHREAD_MUTEX_INITIALIZER). */ +/* (The layout is unnatural to maintain binary compatibility + with earlier releases of LinuxThreads.) */ + typedef struct { - int m_spinlock; /* Spin lock to guarantee mutual exclusion. */ - int m_count; /* 0 if free, > 0 if taken. */ - _pthread_descr m_owner; /* Owner of mutex (for recursive mutexes) */ - int m_kind; /* Kind of mutex */ - struct _pthread_queue m_waiting; /* Threads waiting on this mutex. */ + int m_reserved; /* Reserved for future use */ + int m_count; /* Depth of recursive locking */ + _pthread_descr m_owner; /* Owner thread (if recursive or errcheck) */ + int m_kind; /* Mutex kind: fast, recursive or errcheck */ + struct _pthread_fastlock m_lock; /* Underlying fast lock */ } pthread_mutex_t; #define PTHREAD_MUTEX_INITIALIZER \ @@ -68,21 +71,21 @@ typedef struct /* Conditions (not abstract because of PTHREAD_COND_INITIALIZER */ typedef struct { - int c_spinlock; /* Spin lock to protect the queue. */ - struct _pthread_queue c_waiting; /* Threads waiting on this condition. */ + struct _pthread_fastlock c_lock; /* Protect against concurrent access */ + _pthread_descr c_waiting; /* Threads waiting on this condition */ } pthread_cond_t; -#define PTHREAD_COND_INITIALIZER {0, {0, 0}} +#define PTHREAD_COND_INITIALIZER {{0, 0}, 0} #ifdef __USE_UNIX98 /* Read-write locks. */ typedef struct { - int rw_spinlock; /* Spin lock to guarantee mutual exclusion */ + struct _pthread_fastlock rw_lock; /* Lock to guarantee mutual exclusion */ int rw_readers; /* Number of readers */ _pthread_descr rw_writer; /* Identity of writer, or NULL if none */ - struct _pthread_queue rw_read_waiting; /* Threads waiting for reading */ - struct _pthread_queue rw_write_waiting; /* Threads waiting for writing */ + _pthread_descr rw_read_waiting; /* Threads waiting for reading */ + _pthread_descr rw_write_waiting; /* Threads waiting for writing */ int rw_kind; /* Reader/Writer preference selection */ int rw_pshared; /* Shared between processes or not */ } pthread_rwlock_t; @@ -231,7 +234,8 @@ extern int pthread_detach __P ((pthread_t __th)); /* Functions for handling attributes. */ /* Initialize thread attribute *ATTR with default attributes - (detachstate is PTHREAD_JOINABLE, scheduling policy is SCHED_OTHER). */ + (detachstate is PTHREAD_JOINABLE, scheduling policy is SCHED_OTHER, + no user-provided stack). */ extern int pthread_attr_init __P ((pthread_attr_t *__attr)); /* Destroy thread attribute *ATTR. */ @@ -288,6 +292,7 @@ extern int __pthread_attr_getguardsize __P ((__const pthread_attr_t *__attr, size_t *__guardsize)); extern int pthread_attr_getguardsize __P ((__const pthread_attr_t *__attr, size_t *__guardsize)); +#endif /* Set the starting address of the stack of the thread to be created. Depending on whether the stack grows up or doen the value must either @@ -317,7 +322,6 @@ extern int __pthread_attr_getstacksize __P ((__const pthread_attr_t *__attr, size_t *__stacksize)); extern int pthread_attr_getstacksize __P ((__const pthread_attr_t *__attr, size_t *__stacksize)); -#endif /* Functions for scheduling control. */ diff --git a/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h b/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h index feb4e10c6e..fe68ab7183 100644 --- a/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h +++ b/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h @@ -1,5 +1,5 @@ /* Define POSIX options for Linux. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -82,6 +82,12 @@ /* We provide priority scheduling for threads. */ #define _POSIX_THREAD_PRIORITY_SCHEDULING 1 +/* We support user-defined stack sizes. */ +#define _POSIX_THREAD_ATTR_STACKSIZE 1 + +/* We support user-defined stacks. */ +#define _POSIX_THREAD_ATTR_STACKADDR 1 + /* We support POSIX.1b semaphores, but only the non-shared form for now. */ /*#define _POSIX_SEMAPHORES 1 XXX We are not quite there now. */ diff --git a/sysdeps/unix/sysv/linux/bits/sched.h b/sysdeps/unix/sysv/linux/bits/sched.h index 0ac5409172..187039b2dc 100644 --- a/sysdeps/unix/sysv/linux/bits/sched.h +++ b/sysdeps/unix/sysv/linux/bits/sched.h @@ -1,6 +1,6 @@ /* Definitions of constants and data structure for POSIX 1003.1b-1993 scheduling interface. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -38,10 +38,11 @@ struct sched_param /* Cloning flags. */ # define CSIGNAL 0x000000ff /* Signal mask to be sent at exit. */ # define CLONE_VM 0x00000100 /* Set if VM shared between processes. */ -# define CLONE_FS 0x00000200 /* Set if fs info shared between processes.*/ -# define CLONE_FILES 0x00000400 /* Set if open files shared between processes*/ +# define CLONE_FS 0x00000200 /* Set if fs info shared between processes. */ +# define CLONE_FILES 0x00000400 /* Set if open files shared between processes. */ # define CLONE_SIGHAND 0x00000800 /* Set if signal handlers shared. */ # define CLONE_PID 0x00001000 /* Set if pid shared. */ +# define CLONE_PTRACE 0x00002000 /* Set if tracing continues on the child. */ #endif