diff options
| author | thorpej <thorpej@NetBSD.org> | 2003-01-22 22:51:42 +0000 |
|---|---|---|
| committer | thorpej <thorpej@NetBSD.org> | 2003-01-22 22:51:42 +0000 |
| commit | c151b13c1f2409b2d482fd868d9b4bca1be0bfb9 (patch) | |
| tree | bd655799f8afdcbe6c2aa9f94b12079ea737b6ec /lib/libpthread | |
| parent | 2332bdc2f1d54c4302a8913441eaf03dbc2f9fc2 (diff) | |
Rewrite to use our internal scheduling primitives.
Diffstat (limited to 'lib/libpthread')
| -rw-r--r-- | lib/libpthread/sem.c | 425 |
1 files changed, 233 insertions, 192 deletions
diff --git a/lib/libpthread/sem.c b/lib/libpthread/sem.c index 9a10e2337eb..1fe0c09667a 100644 --- a/lib/libpthread/sem.c +++ b/lib/libpthread/sem.c @@ -1,4 +1,40 @@ -/* $NetBSD: sem.c,v 1.2 2003/01/20 20:52:24 christos Exp $ */ +/* $NetBSD: sem.c,v 1.3 2003/01/22 22:51:42 thorpej Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ /* * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. @@ -27,47 +63,37 @@ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: src/lib/libc/sys/sem.c,v 1.3 2003/01/14 03:36:45 tjr Exp $ */ +#include <sys/types.h> +#include <sys/ksem.h> +#include <sys/queue.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <semaphore.h> #include <stdarg.h> -#include <pthread.h> -#include <semaphore.h> -#include <sys/queue.h> -#include <sys/ksem.h> +#include "pthread.h" +#include "pthread_int.h" -/* - * Semaphore definitions. - */ struct _sem_st { -#define SEM_MAGIC ((u_int32_t) 0x09fa4012) - u_int32_t magic; - pthread_mutex_t lock; - pthread_cond_t gtzero; - u_int32_t count; - u_int32_t nwaiters; -#define SEM_USER (NULL) - semid_t semid; /* semaphore id if kernel (shared) semaphore */ - int syssem; /* 1 if kernel (shared) semaphore */ - LIST_ENTRY(_sem_st) entry; - struct _sem_st **backpointer; -}; + unsigned int usem_magic; +#define USEM_MAGIC 0x09fa4012 + LIST_ENTRY(_sem_st) usem_list; + semid_t usem_semid; /* 0 -> user (non-shared) */ +#define USEM_USER 0 /* assumes kernel does not use NULL */ + sem_t *usem_identity; -#define _SEM_CHECK_VALIDITY(sem) \ - if ((*(sem))->magic != SEM_MAGIC) { \ - errno = EINVAL; \ - retval = -1; \ - goto RETURN; \ - } + /* Protects data below. */ + pthread_spin_t usem_interlock; -static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); + struct pthread_queue_t usem_waiters; + unsigned int usem_count; +}; + +static int sem_alloc(unsigned int value, semid_t semid, sem_t *semp); static void sem_free(sem_t sem); static LIST_HEAD(, _sem_st) named_sems = LIST_HEAD_INITIALIZER(&named_sems); @@ -77,173 +103,143 @@ static void sem_free(sem_t sem) { - pthread_mutex_destroy(&sem->lock); - pthread_cond_destroy(&sem->gtzero); - sem->magic = 0; + sem->usem_magic = 0; free(sem); } -static sem_t -sem_alloc(unsigned int value, semid_t semid, int system_sem) +static int +sem_alloc(unsigned int value, semid_t semid, sem_t *semp) { sem_t sem; - if (value > SEM_VALUE_MAX) { - errno = EINVAL; - return (NULL); - } + if (value > SEM_VALUE_MAX) + return (EINVAL); - sem = (sem_t)malloc(sizeof(struct _sem_st)); - if (sem == NULL) { - errno = ENOSPC; - return (NULL); - } + if ((sem = malloc(sizeof(struct _sem_st))) == NULL) + return (ENOSPC); - /* - * Initialize the semaphore. - */ - if (pthread_mutex_init(&sem->lock, NULL) != 0) { - free(sem); - errno = ENOSPC; - return (NULL); - } + sem->usem_magic = USEM_MAGIC; + pthread_lockinit(&sem->usem_interlock); + PTQ_INIT(&sem->usem_waiters); + sem->usem_count = value; + sem->usem_semid = semid; - if (pthread_cond_init(&sem->gtzero, NULL) != 0) { - pthread_mutex_destroy(&sem->lock); - free(sem); - errno = ENOSPC; - return (NULL); - } - - sem->count = (u_int32_t)value; - sem->nwaiters = 0; - sem->magic = SEM_MAGIC; - sem->semid = semid; - sem->syssem = system_sem; - return (sem); + *semp = sem; + return (0); } int sem_init(sem_t *sem, int pshared, unsigned int value) { - int retval, got_system_sem; semid_t semid; + int error; - got_system_sem = 0; - semid = SEM_USER; - /* - * Range check the arguments. - */ - if (pshared != 0) { - retval = _ksem_init(value, &semid); - if (retval == -1) - goto RETURN; - got_system_sem = 1; - } + semid = USEM_USER; - (*sem) = sem_alloc(value, semid, got_system_sem); - if ((*sem) == NULL) - retval = -1; - else - retval = 0; - RETURN: - if (retval != 0 && got_system_sem) + if (pshared && _ksem_init(value, &semid) == -1) + return (-1); + + if ((error = sem_alloc(value, semid, sem)) != 0) { _ksem_destroy(semid); - return retval; + errno = error; + return (-1); + } + + return (0); } int sem_destroy(sem_t *sem) { - int retval; - - _SEM_CHECK_VALIDITY(sem); + pthread_t self; - pthread_mutex_lock(&(*sem)->lock); - /* - * If this is a system semaphore let the kernel track it otherwise - * make sure there are no waiters. - */ - if ((*sem)->syssem != 0) { - retval = _ksem_destroy((*sem)->semid); - if (retval == -1) { - pthread_mutex_unlock(&(*sem)->lock); - goto RETURN; +#ifdef ERRORCHECK + if (sem == NULL || *sem == NULL || (*sem)->usem_magic != USEM_MAGIC) { + errno = EINVAL; + return (-1); + } +#endif + + if ((*sem)->usem_semid != USEM_USER) { + if (_ksem_destroy((*sem)->usem_semid)) + return (-1); + } else { + self = pthread__self(); + pthread_spinlock(self, &(*sem)->usem_interlock); + if (!PTQ_EMPTY(&(*sem)->usem_waiters)) { + pthread_spinunlock(self, &(*sem)->usem_interlock); + errno = EBUSY; + return (-1); } - } else if ((*sem)->nwaiters > 0) { - pthread_mutex_unlock(&(*sem)->lock); - errno = EBUSY; - retval = -1; - goto RETURN; + pthread_spinunlock(self, &(*sem)->usem_interlock); } - pthread_mutex_unlock(&(*sem)->lock); - sem_free(*sem); + sem_free(*sem); - retval = 0; - RETURN: - return retval; + return (0); } sem_t * sem_open(const char *name, int oflag, ...) { - sem_t *sem; - sem_t s; + sem_t *sem, s; semid_t semid; mode_t mode; unsigned int value; + int error; + va_list ap; mode = 0; value = 0; - if ((oflag & O_CREAT) != 0) { - va_list ap; - + if (oflag & O_CREAT) { va_start(ap, oflag); mode = va_arg(ap, int); value = va_arg(ap, unsigned int); va_end(ap); } + /* - * we can be lazy and let the kernel handle the "oflag", + * We can be lazy and let the kernel handle the oflag, * we'll just merge duplicate IDs into our list. */ if (_ksem_open(name, oflag, mode, value, &semid) == -1) return (SEM_FAILED); + /* - * search for a duplicate ID, we must return the same sem_t * + * Search for a duplicate ID, we must return the same sem_t * * if we locate one. */ pthread_mutex_lock(&named_sems_mtx); - LIST_FOREACH(s, &named_sems, entry) { - if (s->semid == semid) { - pthread_mutex_unlock(&named_sems_mtx); - return (s->backpointer); - } + LIST_FOREACH(s, &named_sems, usem_list) { + if (s->usem_semid == semid) { + pthread_mutex_unlock(&named_sems_mtx); + return (s->usem_identity); + } } - sem = (sem_t *)malloc(sizeof(*sem)); - if (sem == NULL) - goto err; - *sem = sem_alloc(value, semid, 1); - if ((*sem) == NULL) - goto err; - LIST_INSERT_HEAD(&named_sems, *sem, entry); - (*sem)->backpointer = sem; + + if ((sem = malloc(sizeof(*sem))) == NULL) { + error = ENOSPC; + goto bad; + } + if ((error = sem_alloc(value, semid, sem)) != 0) + goto bad; + + LIST_INSERT_HEAD(&named_sems, *sem, usem_list); + (*sem)->usem_identity = sem; pthread_mutex_unlock(&named_sems_mtx); + return (sem); -err: + + bad: pthread_mutex_unlock(&named_sems_mtx); _ksem_close(semid); if (sem != NULL) { if (*sem != NULL) sem_free(*sem); - else - errno = ENOSPC; free(sem); - } else { - errno = ENOSPC; } + errno = error; return (SEM_FAILED); } @@ -251,16 +247,24 @@ int sem_close(sem_t *sem) { - if ((*sem)->syssem == 0) { +#ifdef ERRORCHECK + if (sem == NULL || *sem == NULL || (*sem)->usem_magic != USEM_MAGIC) { errno = EINVAL; return (-1); } +#endif + + if ((*sem)->usem_semid == USEM_USER) { + errno = EINVAL; + return (-1); + } + pthread_mutex_lock(&named_sems_mtx); - if (_ksem_close((*sem)->semid) == -1) { + if (_ksem_close((*sem)->usem_semid) == -1) { pthread_mutex_unlock(&named_sems_mtx); return (-1); } - LIST_REMOVE((*sem), entry); + LIST_REMOVE((*sem), usem_list); pthread_mutex_unlock(&named_sems_mtx); sem_free(*sem); free(sem); @@ -277,101 +281,138 @@ sem_unlink(const char *name) int sem_wait(sem_t *sem) { - int retval; + pthread_t self; + +#ifdef ERRORCHECK + if (sem == NULL || *sem == NULL || (*sem)->usem_magic != USEM_MAGIC) { + errno = EINVAL; + return (-1); + } +#endif - _SEM_CHECK_VALIDITY(sem); + self = pthread__self(); - if ((*sem)->syssem != 0) { - retval = _ksem_wait((*sem)->semid); - goto RETURN; + if ((*sem)->usem_semid != USEM_USER) { + pthread__testcancel(self); + return (_ksem_wait((*sem)->usem_semid)); } - pthread_mutex_lock(&(*sem)->lock); + for (;;) { + pthread_spinlock(self, &(*sem)->usem_interlock); + pthread_spinlock(self, &self->pt_statelock); + if (self->pt_cancel) { + pthread_spinunlock(self, &self->pt_statelock); + pthread_spinunlock(self, &(*sem)->usem_interlock); + pthread_exit(PTHREAD_CANCELED); + } + + if ((*sem)->usem_count > 0) { + pthread_spinunlock(self, &self->pt_statelock); + break; + } - while ((*sem)->count == 0) { - (*sem)->nwaiters++; - pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock); - (*sem)->nwaiters--; + PTQ_INSERT_TAIL(&(*sem)->usem_waiters, self, pt_sleep); + self->pt_state = PT_STATE_BLOCKED_QUEUE; + self->pt_sleepobj = *sem; + self->pt_sleepq = &(*sem)->usem_waiters; + self->pt_sleeplock = &(*sem)->usem_interlock; + pthread_spinunlock(self, &self->pt_statelock); + + /* XXX What about signals? */ + + pthread__block(self, &(*sem)->usem_interlock); + /* interlock is not held when we return */ } - (*sem)->count--; - pthread_mutex_unlock(&(*sem)->lock); + (*sem)->usem_count--; + + pthread_spinunlock(self, &(*sem)->usem_interlock); - retval = 0; - RETURN: - return retval; + return (0); } int sem_trywait(sem_t *sem) { - int retval; - - _SEM_CHECK_VALIDITY(sem); + pthread_t self; - if ((*sem)->syssem != 0) { - retval = _ksem_trywait((*sem)->semid); - goto RETURN; +#ifdef ERRORCHECK + if (sem == NULL || *sem == NULL || (*sem)->usem_magic != USEM_MAGIC) { + errno = EINVAL; + return (-1); } +#endif - pthread_mutex_lock(&(*sem)->lock); + if ((*sem)->usem_semid != USEM_USER) + return (_ksem_trywait((*sem)->usem_semid)); - if ((*sem)->count > 0) { - (*sem)->count--; - retval = 0; - } else { + self = pthread__self(); + + pthread_spinlock(self, &(*sem)->usem_interlock); + + if ((*sem)->usem_count == 0) { + pthread_spinunlock(self, &(*sem)->usem_interlock); errno = EAGAIN; - retval = -1; + return (-1); } - - pthread_mutex_unlock(&(*sem)->lock); - RETURN: - return retval; + (*sem)->usem_count--; + + pthread_spinunlock(self, &(*sem)->usem_interlock); + + return (0); } int sem_post(sem_t *sem) { - int retval; - - _SEM_CHECK_VALIDITY(sem); + pthread_t self, blocked; - if ((*sem)->syssem != 0) { - retval = _ksem_post((*sem)->semid); - goto RETURN; +#ifdef ERRORCHECK + if (sem == NULL || *sem == NULL || (*sem)->usem_magic != USEM_MAGIC) { + errno = EINVAL; + return (-1); } +#endif + + if ((*sem)->usem_semid != USEM_USER) + return (_ksem_post((*sem)->usem_semid)); - pthread_mutex_lock(&(*sem)->lock); + self = pthread__self(); - (*sem)->count++; - if ((*sem)->nwaiters > 0) - pthread_cond_signal(&(*sem)->gtzero); + pthread_spinlock(self, &(*sem)->usem_interlock); + (*sem)->usem_count++; + blocked = PTQ_FIRST(&(*sem)->usem_waiters); + if (blocked) + PTQ_REMOVE(&(*sem)->usem_waiters, blocked, pt_sleep); + pthread_spinunlock(self, &(*sem)->usem_interlock); - pthread_mutex_unlock(&(*sem)->lock); + /* Give the head of the blocked queue another try. */ + if (blocked) + pthread__sched(self, blocked); - retval = 0; - RETURN: - return retval; + return (0); } int sem_getvalue(sem_t * __restrict sem, int * __restrict sval) { - int retval; - - _SEM_CHECK_VALIDITY(sem); + pthread_t self; - if ((*sem)->syssem != 0) { - retval = _ksem_getvalue((*sem)->semid, sval); - goto RETURN; +#ifdef ERRORCHECK + if (sem == NULL || *sem == NULL || (*sem)->usem_magic != USEM_MAGIC) { + errno = EINVAL; + return (-1); } +#endif + if ((*sem)->usem_semid != USEM_USER) + return (_ksem_getvalue((*sem)->usem_semid, sval)); + + self = pthread__self(); - pthread_mutex_lock(&(*sem)->lock); - *sval = (int)(*sem)->count; - pthread_mutex_unlock(&(*sem)->lock); + pthread_spinlock(self, &(*sem)->usem_interlock); + *sval = (int) (*sem)->usem_count; + pthread_spinunlock(self, &(*sem)->usem_interlock); - retval = 0; - RETURN: - return retval; + return (0); } |
