From 2ab83179b5b31c53a1b76ff437e1d075d64a3a95 Mon Sep 17 00:00:00 2001 From: David Xu Date: Thu, 4 Sep 2003 14:06:43 +0000 Subject: [PATCH] Add code to support barrier synchronous object and implement pthread_mutex_timedlock(). Reviewed by: deischen --- include/pthread.h | 16 ++++ lib/libkse/thread/Makefile.inc | 2 + lib/libkse/thread/thr_barrier.c | 122 ++++++++++++++++++++++++ lib/libkse/thread/thr_barrierattr.c | 93 ++++++++++++++++++ lib/libkse/thread/thr_kern.c | 7 +- lib/libkse/thread/thr_mutex.c | 116 +++++++++++++++++++++- lib/libkse/thread/thr_private.h | 12 +++ lib/libpthread/pthread.map | 17 ++++ lib/libpthread/thread/Makefile.inc | 2 + lib/libpthread/thread/thr_barrier.c | 122 ++++++++++++++++++++++++ lib/libpthread/thread/thr_barrierattr.c | 93 ++++++++++++++++++ lib/libpthread/thread/thr_kern.c | 7 +- lib/libpthread/thread/thr_mutex.c | 116 +++++++++++++++++++++- lib/libpthread/thread/thr_private.h | 12 +++ 14 files changed, 725 insertions(+), 12 deletions(-) create mode 100644 lib/libkse/thread/thr_barrier.c create mode 100644 lib/libkse/thread/thr_barrierattr.c create mode 100644 lib/libpthread/thread/thr_barrier.c create mode 100644 lib/libpthread/thread/thr_barrierattr.c diff --git a/include/pthread.h b/include/pthread.h index 2ac07e0cc295..9da241208667 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -52,6 +52,7 @@ #define PTHREAD_KEYS_MAX 256 #define PTHREAD_STACK_MIN 1024 #define PTHREAD_THREADS_MAX ULONG_MAX +#define PTHREAD_BARRIER_SERIAL_THREAD -1 /* * Flags for threads and thread attributes. @@ -95,6 +96,8 @@ struct pthread_mutex_attr; struct pthread_once; struct pthread_rwlock; struct pthread_rwlockattr; +struct pthread_barrier; +struct pthread_barrier_attr; /* * Primitive system data type definitions required by P1003.1c. @@ -113,6 +116,8 @@ typedef int pthread_key_t; typedef struct pthread_once pthread_once_t; typedef struct pthread_rwlock *pthread_rwlock_t; typedef struct pthread_rwlockattr *pthread_rwlockattr_t; +typedef struct pthread_barrier *pthread_barrier_t; +typedef struct pthread_barrierattr *pthread_barrierattr_t; /* * Additional type definitions: @@ -203,6 +208,15 @@ int pthread_attr_setguardsize(pthread_attr_t *, size_t); int pthread_attr_setstack(pthread_attr_t *, void *, size_t); int pthread_attr_setstackaddr(pthread_attr_t *, void *); int pthread_attr_setdetachstate(pthread_attr_t *, int); +int pthread_barrier_destroy(pthread_barrier_t *); +int pthread_barrier_init(pthread_barrier_t *, + const pthread_barrierattr_t *, unsigned); +int pthread_barrier_wait(pthread_barrier_t *); +int pthread_barrierattr_destroy(pthread_barrierattr_t *); +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, + int *); +int pthread_barrierattr_init(pthread_barrierattr_t *); +int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int); void pthread_cleanup_pop(int); void pthread_cleanup_push(void (*) (void *), void *routine_arg); int pthread_condattr_destroy(pthread_condattr_t *); @@ -236,6 +250,8 @@ int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); int pthread_mutex_lock(pthread_mutex_t *); int pthread_mutex_trylock(pthread_mutex_t *); +int pthread_mutex_timedlock(pthread_mutex_t *, + const struct timespec *); int pthread_mutex_unlock(pthread_mutex_t *); int pthread_once(pthread_once_t *, void (*) (void)); int pthread_rwlock_destroy(pthread_rwlock_t *); diff --git a/lib/libkse/thread/Makefile.inc b/lib/libkse/thread/Makefile.inc index 328c3c815445..70f88cd50bf1 100644 --- a/lib/libkse/thread/Makefile.inc +++ b/lib/libkse/thread/Makefile.inc @@ -28,6 +28,8 @@ SRCS+= \ thr_attr_setstack.c \ thr_attr_setstackaddr.c \ thr_attr_setstacksize.c \ + thr_barrier.c \ + thr_barrierattr.c \ thr_cancel.c \ thr_clean.c \ thr_close.c \ diff --git a/lib/libkse/thread/thr_barrier.c b/lib/libkse/thread/thr_barrier.c new file mode 100644 index 000000000000..612acb1c57d8 --- /dev/null +++ b/lib/libkse/thread/thr_barrier.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2003 David Xu + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include "namespace.h" +#include +#include "un-namespace.h" +#include "thr_private.h" + +__weak_reference(_pthread_barrier_init, pthread_barrier_init); +__weak_reference(_pthread_barrier_wait, pthread_barrier_wait); +__weak_reference(_pthread_barrier_destroy, pthread_barrier_destroy); + +int +_pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_barrier_t bar; + int ret, ret2; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + if (bar->b_waiters > 0) + return (EBUSY); + *barrier = NULL; + ret = _pthread_mutex_destroy(&bar->b_lock); + ret2 = _pthread_cond_destroy(&bar->b_cond); + free(bar); + return (ret ? ret : ret2); +} + +int +_pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, int count) +{ + pthread_barrier_t bar; + int ret; + + if (barrier == NULL || count <= 0) + return (EINVAL); + + bar = malloc(sizeof(struct pthread_barrier)); + if (bar == NULL) + return (ENOMEM); + + if ((ret = _pthread_mutex_init(&bar->b_lock, NULL)) != 0) { + free(bar); + return (ret); + } + + if ((ret = _pthread_cond_init(&bar->b_cond, NULL)) != 0) { + _pthread_mutex_destroy(&bar->b_lock); + free(bar); + return (ret); + } + + bar->b_waiters = 0; + bar->b_count = count; + bar->b_generation = 0; + *barrier = bar; + + return (0); +} + +int +_pthread_barrier_wait(pthread_barrier_t *barrier) +{ + int ret, gen; + pthread_barrier_t bar; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + if ((ret = _pthread_mutex_lock(&bar->b_lock)) != 0) + return (ret); + + if (++bar->b_waiters == bar->b_count) { + /* Current thread is lastest thread */ + bar->b_generation++; + bar->b_waiters = 0; + ret = _pthread_cond_broadcast(&bar->b_cond); + if (ret == 0) + ret = PTHREAD_BARRIER_SERIAL_THREAD; + } else { + gen = bar->b_generation; + do { + ret = _pthread_cond_wait( + &bar->b_cond, &bar->b_lock); + /* test generation to avoid bogus wakeup */ + } while (ret == 0 && gen == bar->b_generation); + } + _pthread_mutex_unlock(&bar->b_lock); + return (ret); +} diff --git a/lib/libkse/thread/thr_barrierattr.c b/lib/libkse/thread/thr_barrierattr.c new file mode 100644 index 000000000000..f71c0ddf0551 --- /dev/null +++ b/lib/libkse/thread/thr_barrierattr.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2003 David Xu . + * All rights reserved. + * + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include "thr_private.h" + +__weak_reference(_pthread_barrierattr_destroy, pthread_barrierattr_destroy); +__weak_reference(_pthread_barrierattr_init, pthread_barrierattr_init); +__weak_reference(_pthread_barrierattr_setpshared, + pthread_barrierattr_setpshared); +__weak_reference(_pthread_barrierattr_getpshared, + pthread_barrierattr_getpshared); + +int +_pthread_barrierattr_destroy(pthread_barrierattr_t *attr) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + free(*attr); + return (0); +} + +int +_pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *pshared) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + *pshared = (*attr)->pshared; + return (0); +} + +int +_pthread_barrierattr_init(pthread_barrierattr_t *attr) +{ + + if (attr == NULL) + return (EINVAL); + + if ((*attr = malloc(sizeof(struct pthread_barrierattr))) == NULL) + return (ENOMEM); + + (*attr)->pshared = PTHREAD_PROCESS_PRIVATE; + return (0); +} + +int +_pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return (EINVAL); + + (*attr)->pshared = pshared; + return (0); +} diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c index 5ed144ae232c..4d308c31314d 100644 --- a/lib/libkse/thread/thr_kern.c +++ b/lib/libkse/thread/thr_kern.c @@ -853,8 +853,9 @@ kse_sched_single(struct kse_mailbox *kmbx) curthread->wakeup_time.tv_nsec = -1; break; - case PS_JOIN: case PS_MUTEX_WAIT: + break; + case PS_JOIN: case PS_SUSPENDED: case PS_DEADLOCK: default: @@ -1735,8 +1736,10 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) case PS_SIGWAIT: KSE_WAITQ_INSERT(kse, thread); break; - case PS_JOIN: case PS_MUTEX_WAIT: + KSE_WAITQ_INSERT(kse, thread); + break; + case PS_JOIN: case PS_SIGSUSPEND: case PS_SUSPENDED: case PS_DEADLOCK: diff --git a/lib/libkse/thread/thr_mutex.c b/lib/libkse/thread/thr_mutex.c index 00da0f0270bb..177a296688f8 100644 --- a/lib/libkse/thread/thr_mutex.c +++ b/lib/libkse/thread/thr_mutex.c @@ -64,6 +64,8 @@ #define THR_ASSERT_NOT_IN_SYNCQ(thr) #endif +#define THR_IN_MUTEXQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) + /* * Prototypes */ @@ -86,6 +88,7 @@ static pthread_mutexattr_t static_mattr = &static_mutex_attr; /* Single underscore versions provided for libc internal usage: */ __weak_reference(__pthread_mutex_lock, pthread_mutex_lock); +__weak_reference(__pthread_mutex_timedlock, pthread_mutex_timedlock); __weak_reference(__pthread_mutex_trylock, pthread_mutex_trylock); /* No difference between libc and application usage of these: */ @@ -448,15 +451,22 @@ _pthread_mutex_trylock(pthread_mutex_t *mutex) } static int -mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) +mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m, + const struct timespec * abstime) { int ret = 0; THR_ASSERT((m != NULL) && (*m != NULL), "Uninitialized mutex in pthread_mutex_trylock_basic"); + if (abstime != NULL && (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000)) + return (EINVAL); + /* Reset the interrupted flag: */ curthread->interrupted = 0; + curthread->timeout = 0; + curthread->wakeup_time.tv_sec = -1; /* * Enter a loop waiting to become the mutex owner. We need a @@ -501,6 +511,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*m)->m_lock); } else { + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + /* * Join the queue of threads waiting to lock * the mutex and save a pointer to the mutex. @@ -521,6 +539,13 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Schedule the next thread: */ _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } } break; @@ -560,6 +585,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*m)->m_lock); } else { + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + /* * Join the queue of threads waiting to lock * the mutex and save a pointer to the mutex. @@ -585,6 +618,13 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Schedule the next thread: */ _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } } break; @@ -634,6 +674,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*m)->m_lock); } else { + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + /* * Join the queue of threads waiting to lock * the mutex and save a pointer to the mutex. @@ -659,6 +707,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Schedule the next thread: */ _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } + /* * The threads priority may have changed while * waiting for the mutex causing a ceiling @@ -680,7 +736,10 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) } } while (((*m)->m_owner != curthread) && (ret == 0) && - (curthread->interrupted == 0)); + (curthread->interrupted == 0) && (curthread->timeout == 0)); + + if (ret == 0 && curthread->timeout) + ret = ETIMEDOUT; /* * Check to see if this thread was interrupted and @@ -720,7 +779,7 @@ __pthread_mutex_lock(pthread_mutex_t *m) * initialization: */ else if ((*m != NULL) || ((ret = init_static(curthread, m)) == 0)) - ret = mutex_lock_common(curthread, m); + ret = mutex_lock_common(curthread, m, NULL); return (ret); } @@ -746,7 +805,56 @@ _pthread_mutex_lock(pthread_mutex_t *m) */ else if ((*m != NULL) || ((ret = init_static_private(curthread, m)) == 0)) - ret = mutex_lock_common(curthread, m); + ret = mutex_lock_common(curthread, m, NULL); + + return (ret); +} + +int +__pthread_mutex_timedlock(pthread_mutex_t *m, + const struct timespec *abs_timeout) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + + curthread = _get_curthread(); + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization: + */ + else if ((*m != NULL) || ((ret = init_static(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, abs_timeout); + + return (ret); +} + +int +_pthread_mutex_timedlock(pthread_mutex_t *m, + const struct timespec *abs_timeout) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + curthread = _get_curthread(); + + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization marking it private (delete safe): + */ + else if ((*m != NULL) || + ((ret = init_static_private(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, abs_timeout); return (ret); } diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h index 63022994ca43..21276190ef61 100644 --- a/lib/libkse/thread/thr_private.h +++ b/lib/libkse/thread/thr_private.h @@ -390,6 +390,18 @@ struct pthread_cond_attr { long c_flags; }; +struct pthread_barrier { + pthread_mutex_t b_lock; + pthread_cond_t b_cond; + int b_count; + int b_waiters; + int b_generation; +}; + +struct pthread_barrierattr { + int pshared; +}; + /* * Flags for condition variables. */ diff --git a/lib/libpthread/pthread.map b/lib/libpthread/pthread.map index a2f65a416909..62f4635d630b 100644 --- a/lib/libpthread/pthread.map +++ b/lib/libpthread/pthread.map @@ -14,6 +14,7 @@ global: __pthread_cond_wait; __pthread_mutex_lock; __pthread_mutex_trylock; + __pthread_mutex_timedlock; __read; __readv; __select; @@ -26,6 +27,13 @@ global: _nanosleep; _pause; _pselect; + _pthread_barrier_destroy; + _pthread_barrier_init; + _pthread_barrier_wait; + _pthread_barrierattr_destroy; + _pthread_barrierattr_getpshared; + _pthread_barrierattr_init; + _pthread_barrierattr_setpshared; _pthread_attr_default; _pthread_attr_destroy; _pthread_attr_get_np; @@ -80,6 +88,7 @@ global: _pthread_mutex_init; _pthread_mutex_lock; _pthread_mutex_setprioceiling; + _pthread_mutex_timedlock; _pthread_mutex_trylock; _pthread_mutex_unlock; _pthread_mutexattr_default; @@ -162,6 +171,13 @@ global: pause; poll; pselect; + pthread_barrier_destroy; + pthread_barrier_init; + pthread_barrier_wait; + pthread_barrierattr_destroy; + pthread_barrierattr_getpshared; + pthread_barrierattr_init; + pthread_barrierattr_setpshared; pthread_attr_destroy; pthread_attr_get_np; pthread_attr_getdetachstate; @@ -214,6 +230,7 @@ global: pthread_mutex_init; pthread_mutex_lock; pthread_mutex_setprioceiling; + pthread_mutex_timedlock; pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy; diff --git a/lib/libpthread/thread/Makefile.inc b/lib/libpthread/thread/Makefile.inc index 328c3c815445..70f88cd50bf1 100644 --- a/lib/libpthread/thread/Makefile.inc +++ b/lib/libpthread/thread/Makefile.inc @@ -28,6 +28,8 @@ SRCS+= \ thr_attr_setstack.c \ thr_attr_setstackaddr.c \ thr_attr_setstacksize.c \ + thr_barrier.c \ + thr_barrierattr.c \ thr_cancel.c \ thr_clean.c \ thr_close.c \ diff --git a/lib/libpthread/thread/thr_barrier.c b/lib/libpthread/thread/thr_barrier.c new file mode 100644 index 000000000000..612acb1c57d8 --- /dev/null +++ b/lib/libpthread/thread/thr_barrier.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2003 David Xu + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include "namespace.h" +#include +#include "un-namespace.h" +#include "thr_private.h" + +__weak_reference(_pthread_barrier_init, pthread_barrier_init); +__weak_reference(_pthread_barrier_wait, pthread_barrier_wait); +__weak_reference(_pthread_barrier_destroy, pthread_barrier_destroy); + +int +_pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_barrier_t bar; + int ret, ret2; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + if (bar->b_waiters > 0) + return (EBUSY); + *barrier = NULL; + ret = _pthread_mutex_destroy(&bar->b_lock); + ret2 = _pthread_cond_destroy(&bar->b_cond); + free(bar); + return (ret ? ret : ret2); +} + +int +_pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, int count) +{ + pthread_barrier_t bar; + int ret; + + if (barrier == NULL || count <= 0) + return (EINVAL); + + bar = malloc(sizeof(struct pthread_barrier)); + if (bar == NULL) + return (ENOMEM); + + if ((ret = _pthread_mutex_init(&bar->b_lock, NULL)) != 0) { + free(bar); + return (ret); + } + + if ((ret = _pthread_cond_init(&bar->b_cond, NULL)) != 0) { + _pthread_mutex_destroy(&bar->b_lock); + free(bar); + return (ret); + } + + bar->b_waiters = 0; + bar->b_count = count; + bar->b_generation = 0; + *barrier = bar; + + return (0); +} + +int +_pthread_barrier_wait(pthread_barrier_t *barrier) +{ + int ret, gen; + pthread_barrier_t bar; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + if ((ret = _pthread_mutex_lock(&bar->b_lock)) != 0) + return (ret); + + if (++bar->b_waiters == bar->b_count) { + /* Current thread is lastest thread */ + bar->b_generation++; + bar->b_waiters = 0; + ret = _pthread_cond_broadcast(&bar->b_cond); + if (ret == 0) + ret = PTHREAD_BARRIER_SERIAL_THREAD; + } else { + gen = bar->b_generation; + do { + ret = _pthread_cond_wait( + &bar->b_cond, &bar->b_lock); + /* test generation to avoid bogus wakeup */ + } while (ret == 0 && gen == bar->b_generation); + } + _pthread_mutex_unlock(&bar->b_lock); + return (ret); +} diff --git a/lib/libpthread/thread/thr_barrierattr.c b/lib/libpthread/thread/thr_barrierattr.c new file mode 100644 index 000000000000..f71c0ddf0551 --- /dev/null +++ b/lib/libpthread/thread/thr_barrierattr.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2003 David Xu . + * All rights reserved. + * + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include "thr_private.h" + +__weak_reference(_pthread_barrierattr_destroy, pthread_barrierattr_destroy); +__weak_reference(_pthread_barrierattr_init, pthread_barrierattr_init); +__weak_reference(_pthread_barrierattr_setpshared, + pthread_barrierattr_setpshared); +__weak_reference(_pthread_barrierattr_getpshared, + pthread_barrierattr_getpshared); + +int +_pthread_barrierattr_destroy(pthread_barrierattr_t *attr) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + free(*attr); + return (0); +} + +int +_pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *pshared) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + *pshared = (*attr)->pshared; + return (0); +} + +int +_pthread_barrierattr_init(pthread_barrierattr_t *attr) +{ + + if (attr == NULL) + return (EINVAL); + + if ((*attr = malloc(sizeof(struct pthread_barrierattr))) == NULL) + return (ENOMEM); + + (*attr)->pshared = PTHREAD_PROCESS_PRIVATE; + return (0); +} + +int +_pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return (EINVAL); + + (*attr)->pshared = pshared; + return (0); +} diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c index 5ed144ae232c..4d308c31314d 100644 --- a/lib/libpthread/thread/thr_kern.c +++ b/lib/libpthread/thread/thr_kern.c @@ -853,8 +853,9 @@ kse_sched_single(struct kse_mailbox *kmbx) curthread->wakeup_time.tv_nsec = -1; break; - case PS_JOIN: case PS_MUTEX_WAIT: + break; + case PS_JOIN: case PS_SUSPENDED: case PS_DEADLOCK: default: @@ -1735,8 +1736,10 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) case PS_SIGWAIT: KSE_WAITQ_INSERT(kse, thread); break; - case PS_JOIN: case PS_MUTEX_WAIT: + KSE_WAITQ_INSERT(kse, thread); + break; + case PS_JOIN: case PS_SIGSUSPEND: case PS_SUSPENDED: case PS_DEADLOCK: diff --git a/lib/libpthread/thread/thr_mutex.c b/lib/libpthread/thread/thr_mutex.c index 00da0f0270bb..177a296688f8 100644 --- a/lib/libpthread/thread/thr_mutex.c +++ b/lib/libpthread/thread/thr_mutex.c @@ -64,6 +64,8 @@ #define THR_ASSERT_NOT_IN_SYNCQ(thr) #endif +#define THR_IN_MUTEXQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) + /* * Prototypes */ @@ -86,6 +88,7 @@ static pthread_mutexattr_t static_mattr = &static_mutex_attr; /* Single underscore versions provided for libc internal usage: */ __weak_reference(__pthread_mutex_lock, pthread_mutex_lock); +__weak_reference(__pthread_mutex_timedlock, pthread_mutex_timedlock); __weak_reference(__pthread_mutex_trylock, pthread_mutex_trylock); /* No difference between libc and application usage of these: */ @@ -448,15 +451,22 @@ _pthread_mutex_trylock(pthread_mutex_t *mutex) } static int -mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) +mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m, + const struct timespec * abstime) { int ret = 0; THR_ASSERT((m != NULL) && (*m != NULL), "Uninitialized mutex in pthread_mutex_trylock_basic"); + if (abstime != NULL && (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000)) + return (EINVAL); + /* Reset the interrupted flag: */ curthread->interrupted = 0; + curthread->timeout = 0; + curthread->wakeup_time.tv_sec = -1; /* * Enter a loop waiting to become the mutex owner. We need a @@ -501,6 +511,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*m)->m_lock); } else { + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + /* * Join the queue of threads waiting to lock * the mutex and save a pointer to the mutex. @@ -521,6 +539,13 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Schedule the next thread: */ _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } } break; @@ -560,6 +585,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*m)->m_lock); } else { + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + /* * Join the queue of threads waiting to lock * the mutex and save a pointer to the mutex. @@ -585,6 +618,13 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Schedule the next thread: */ _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } } break; @@ -634,6 +674,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*m)->m_lock); } else { + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + /* * Join the queue of threads waiting to lock * the mutex and save a pointer to the mutex. @@ -659,6 +707,14 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) /* Schedule the next thread: */ _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } + /* * The threads priority may have changed while * waiting for the mutex causing a ceiling @@ -680,7 +736,10 @@ mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m) } } while (((*m)->m_owner != curthread) && (ret == 0) && - (curthread->interrupted == 0)); + (curthread->interrupted == 0) && (curthread->timeout == 0)); + + if (ret == 0 && curthread->timeout) + ret = ETIMEDOUT; /* * Check to see if this thread was interrupted and @@ -720,7 +779,7 @@ __pthread_mutex_lock(pthread_mutex_t *m) * initialization: */ else if ((*m != NULL) || ((ret = init_static(curthread, m)) == 0)) - ret = mutex_lock_common(curthread, m); + ret = mutex_lock_common(curthread, m, NULL); return (ret); } @@ -746,7 +805,56 @@ _pthread_mutex_lock(pthread_mutex_t *m) */ else if ((*m != NULL) || ((ret = init_static_private(curthread, m)) == 0)) - ret = mutex_lock_common(curthread, m); + ret = mutex_lock_common(curthread, m, NULL); + + return (ret); +} + +int +__pthread_mutex_timedlock(pthread_mutex_t *m, + const struct timespec *abs_timeout) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + + curthread = _get_curthread(); + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization: + */ + else if ((*m != NULL) || ((ret = init_static(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, abs_timeout); + + return (ret); +} + +int +_pthread_mutex_timedlock(pthread_mutex_t *m, + const struct timespec *abs_timeout) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + curthread = _get_curthread(); + + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization marking it private (delete safe): + */ + else if ((*m != NULL) || + ((ret = init_static_private(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, abs_timeout); return (ret); } diff --git a/lib/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h index 63022994ca43..21276190ef61 100644 --- a/lib/libpthread/thread/thr_private.h +++ b/lib/libpthread/thread/thr_private.h @@ -390,6 +390,18 @@ struct pthread_cond_attr { long c_flags; }; +struct pthread_barrier { + pthread_mutex_t b_lock; + pthread_cond_t b_cond; + int b_count; + int b_waiters; + int b_generation; +}; + +struct pthread_barrierattr { + int pshared; +}; + /* * Flags for condition variables. */