diff options
author | Yann Ylavic <ylavic@apache.org> | 2017-04-06 21:57:29 +0000 |
---|---|---|
committer | Yann Ylavic <ylavic@apache.org> | 2017-04-06 21:57:29 +0000 |
commit | c93e1021f0bffe93d548b372b10704b8584c58f9 (patch) | |
tree | 40af729ab76afc3f2785b5f30e60b246d2dfe4ba | |
parent | cb035cfba978f8c8e92b0b013543ea56b9a094f1 (diff) | |
download | apr-c93e1021f0bffe93d548b372b10704b8584c58f9.tar.gz |
Merge r1790296, r1790302, r1790303, r1790304, r1790330, r1790331, r1790436, r1790439, r1790444, r1790446 from trunk:
Follow up to r1667900: semtimedop() should be passed a relative timeout rather
then absolute.
semtimedop() takes a delta time, so accept what is given as the "time remaining"
rr1790301
Use our "portable" versions
Make clear this is a delta timeout
locks: when pthread_mutex_timedlock() isn't available, fall back to an
implementation based on pthread_cond_timedwait() when possible.
Avoid a compiler warning by using system's errno.
locks: follow up to r1790330.
When no native timedlock is available, fall back to a common/generic spin sleep
proc_mutex_spinsleep_timedacquire() based on the configured APR_USE_*_SERIALIZE
trylock.
Otherwise, choose the best timedlock mechanism in the following order:
1. PTHREAD if HAVE_PTHREAD_MUTEX_ROBUST && (HAVE_PTHREAD_MUTEX_TIMEDLOCK
|| HAVE_PTHREAD_CONDATTR_SETPSHARED)
2. SYSV if HAVE_SEMTIMEDOP
3. POSIX if HAVE_SEM_TIMEDWAIT
4. The one of APR_USE_*_SERIALIZE, hence possibly non-robust and/or spinning
with the same robustness as the underlying apr_proc_mutex_trylock() call.
apr_proc_mutex_timedlock() won't return ENOTIMPL anymore.
locks: follow up to r1790330 and r1790436.
unix/misc.c is not needed anymore since we use apr_proc_mutex_trylock()
directly.
locks: follow up to r1790330.
No functional change, more helpers/macros to help identify struct
proc_pthread_mutex_t members.
locks: follow up to r1790330.
Don't try to access proc_pthread_mutex_t's condvar if the mutex was _put[_ex]()
and not _create()d, this is a real pthread_mutex_t.
Submitted by: ylavic, jim, jim, jim, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic
git-svn-id: https://svn.apache.org/repos/asf/apr/apr/branches/1.6.x@1790474 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | configure.in | 5 | ||||
-rw-r--r-- | locks/unix/misc.c | 193 | ||||
-rw-r--r-- | locks/unix/proc_mutex.c | 479 | ||||
-rw-r--r-- | locks/unix/thread_mutex.c | 67 | ||||
-rw-r--r-- | test/testprocmutex.c | 27 |
5 files changed, 470 insertions, 301 deletions
diff --git a/configure.in b/configure.in index cfd79e85b..29fd00d85 100644 --- a/configure.in +++ b/configure.in @@ -2297,6 +2297,11 @@ APR_IFALLYES(header:pthread.h define:PTHREAD_PROCESS_SHARED dnl hasprocpthreadser="1", hasprocpthreadser="0") APR_IFALLYES(header:OS.h func:create_sem, hasbeossem="1", hasbeossem="0") +AC_CHECK_FUNCS(pthread_condattr_setpshared) +APR_IFALLYES(header:pthread.h func:pthread_condattr_setpshared, + have_pthread_condattr_setpshared="1", have_pthread_condattr_setpshared="0") +AC_SUBST(have_pthread_condattr_setpshared) + # See which lock mechanism we'll select by default on this system. # The last APR_DECIDE to execute sets the default. # At this stage, we match the ordering in Apache 1.3 diff --git a/locks/unix/misc.c b/locks/unix/misc.c deleted file mode 100644 index d7018fc3a..000000000 --- a/locks/unix/misc.c +++ /dev/null @@ -1,193 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apr_private.h" -#include "apr_arch_thread_mutex.h" -#define APR_WANT_MEMFUNC -#include "apr_want.h" - -#if APR_HAS_THREADS -#if APR_HAS_SYSVSEM_SERIALIZE -#if !HAVE_SEMTIMEDOP -#include <sys/sem.h> -#endif -#endif -#define SLEEP_TIME_NS 10000000 -#define NANOSECS_PER_SEC 1000000000 -extern int errno; - -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -extern int pthread_mutex_timedlock(pthread_mutex_t * mutex, - const struct timespec *abs_timeout); -/* - * A pthread_mutex_timedlock() impl for OSX/macOS, which lacks the - * real thing. - * NOTE: Unlike the real McCoy, won't return EOWNERDEAD, EDEADLK - * or EOWNERDEAD - */ -int pthread_mutex_timedlock(pthread_mutex_t * mutex, - const struct timespec *abs_timeout) -{ - int rv; - struct timespec remaining, ts, tod; - apr_time_t now; - - remaining = *abs_timeout; - now = apr_time_now(); - tod.tv_sec = apr_time_sec(now); - tod.tv_nsec = apr_time_usec(now) * 1000; /* nanoseconds */ - - remaining.tv_sec -= tod.tv_sec; - if (tod.tv_nsec <= remaining.tv_nsec) { - remaining.tv_nsec -= tod.tv_nsec; - } - else { - remaining.tv_sec--; - remaining.tv_nsec = - (NANOSECS_PER_SEC - (tod.tv_nsec - remaining.tv_nsec)); - } - /* If we had a REALLY small timeout ;) */ - if (remaining.tv_sec < 0) { - return ETIMEDOUT; - } - while ((rv = pthread_mutex_trylock(mutex)) == EBUSY) { - ts.tv_sec = 0; - ts.tv_nsec = (remaining.tv_sec > 0 ? SLEEP_TIME_NS : - (remaining.tv_nsec < - SLEEP_TIME_NS ? remaining.tv_nsec : SLEEP_TIME_NS)); - nanosleep(&ts, &ts); - if (ts.tv_nsec <= remaining.tv_nsec) { - remaining.tv_nsec -= ts.tv_nsec; - } - else { - remaining.tv_sec--; - remaining.tv_nsec = - (NANOSECS_PER_SEC - (ts.tv_nsec - remaining.tv_nsec)); - } - if (remaining.tv_sec < 0 - || (!remaining.tv_sec && remaining.tv_nsec <= SLEEP_TIME_NS)) { - return ETIMEDOUT; - } - } - - return rv; -} -#endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */ - -#if APR_HAS_POSIXSEM_SERIALIZE -#if !HAVE_SEM_TIMEDWAIT -extern int sem_timedwait(sem_t * sem, const struct timespec *abs_timeout); -/* - * A sem_timedwait() impl for OSX/macOS, which lacks the - * real thing. - */ -int sem_timedwait(sem_t * sem, const struct timespec *abs_timeout) -{ - int rv; - struct timespec remaining, ts, tod; - apr_time_t now; - - remaining = *abs_timeout; - now = apr_time_now(); - tod.tv_sec = apr_time_sec(now); - tod.tv_nsec = apr_time_usec(now) * 1000; /* nanoseconds */ - - remaining.tv_sec -= tod.tv_sec; - if (tod.tv_nsec <= remaining.tv_nsec) { - remaining.tv_nsec -= tod.tv_nsec; - } - else { - remaining.tv_sec--; - remaining.tv_nsec = - (NANOSECS_PER_SEC - (tod.tv_nsec - remaining.tv_nsec)); - } - /* If we had a REALLY small timeout ;) */ - if (remaining.tv_sec < 0) { - return ETIMEDOUT; - } - errno = 0; - while (((rv = sem_trywait(sem)) != 0) && (errno == EAGAIN)) { - ts.tv_sec = 0; - ts.tv_nsec = (remaining.tv_sec > 0 ? SLEEP_TIME_NS : - (remaining.tv_nsec < - SLEEP_TIME_NS ? remaining.tv_nsec : SLEEP_TIME_NS)); - nanosleep(&ts, &ts); - if (ts.tv_nsec <= remaining.tv_nsec) { - remaining.tv_nsec -= ts.tv_nsec; - } - else { - remaining.tv_sec--; - remaining.tv_nsec = - (NANOSECS_PER_SEC - (ts.tv_nsec - remaining.tv_nsec)); - } - if (remaining.tv_sec < 0 - || (!remaining.tv_sec && remaining.tv_nsec <= SLEEP_TIME_NS)) { - return ETIMEDOUT; - } - } - return rv; -} -#endif /* HAVE_SEM_TIMEDWAIT */ -#endif /* APR_HAS_POSIXSEM_SERIALIZE */ - -#if APR_HAS_SYSVSEM_SERIALIZE -#if !HAVE_SEMTIMEDOP -extern int semtimedop(int semid, struct sembuf *sops, unsigned nsops, - const struct timespec *timeout); -/* - * A semtimedop() impl for OSX/macOS, which lacks the - * real thing. - */ -int semtimedop(int semid, struct sembuf *sops, unsigned nsops, - const struct timespec *timeout) -{ - int rv; - struct timespec remaining, ts; - struct sembuf proc_mutex_op_try; - - proc_mutex_op_try.sem_num = 0; - proc_mutex_op_try.sem_op = -1; - proc_mutex_op_try.sem_flg = SEM_UNDO | IPC_NOWAIT; - - remaining = *timeout; - errno = 0; - while (((rv = semop(semid, &proc_mutex_op_try, nsops)) != 0) - && (errno == EAGAIN)) { - ts.tv_sec = 0; - ts.tv_nsec = (remaining.tv_sec > 0 ? SLEEP_TIME_NS : - (remaining.tv_nsec < - SLEEP_TIME_NS ? remaining.tv_nsec : SLEEP_TIME_NS)); - nanosleep(&ts, &ts); - if (ts.tv_nsec <= remaining.tv_nsec) { - remaining.tv_nsec -= ts.tv_nsec; - } - else { - remaining.tv_sec--; - remaining.tv_nsec = - (NANOSECS_PER_SEC - (ts.tv_nsec - remaining.tv_nsec)); - } - if (remaining.tv_sec < 0 - || (!remaining.tv_sec && remaining.tv_nsec <= SLEEP_TIME_NS)) { - return ETIMEDOUT; - } - } - return rv; -} -#endif /* HAVE_SEMTIMEDOP */ -#endif /* APR_HAS_SYSVSEM_SERIALIZE */ - - -#endif /* APR_HAS_THREADS */ diff --git a/locks/unix/proc_mutex.c b/locks/unix/proc_mutex.c index c4ec910ee..3508b4f04 100644 --- a/locks/unix/proc_mutex.c +++ b/locks/unix/proc_mutex.c @@ -46,6 +46,56 @@ static apr_status_t proc_mutex_no_perms_set(apr_proc_mutex_t *mutex, } #endif +#if APR_HAS_FCNTL_SERIALIZE \ + || APR_HAS_FLOCK_SERIALIZE \ + || (APR_HAS_SYSVSEM_SERIALIZE \ + && !defined(HAVE_SEMTIMEDOP)) \ + || (APR_HAS_POSIXSEM_SERIALIZE \ + && !defined(HAVE_SEM_TIMEDWAIT)) \ + || (APR_HAS_PROC_PTHREAD_SERIALIZE \ + && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) \ + && !defined(HAVE_PTHREAD_CONDATTR_SETPSHARED)) +static apr_status_t proc_mutex_spinsleep_timedacquire(apr_proc_mutex_t *mutex, + apr_time_t timeout, + int absolute) +{ + apr_status_t rv; + if (absolute) { + timeout -= apr_time_now(); + if (timeout < 0) { + timeout = 0; + } + } + if (timeout < 0) { + rv = apr_proc_mutex_lock(mutex); + } + else { +#define SLEEP_TIME apr_time_from_msec(10) + for (;;) { + rv = apr_proc_mutex_trylock(mutex); + if (!APR_STATUS_IS_EBUSY(rv)) { + if (rv == APR_SUCCESS) { + mutex->curr_locked = 1; + } + break; + } + if (!timeout) { + rv = APR_TIMEUP; + break; + } + if (timeout > SLEEP_TIME) { + apr_sleep(SLEEP_TIME); + timeout -= SLEEP_TIME; + } + else { + apr_sleep(timeout); + timeout = 0; + } + } + } + return rv; +} +#endif #if APR_HAS_POSIXSEM_SERIALIZE @@ -183,13 +233,11 @@ static apr_status_t proc_mutex_posix_tryacquire(apr_proc_mutex_t *mutex) return APR_SUCCESS; } +#if defined(HAVE_SEM_TIMEDWAIT) static apr_status_t proc_mutex_posix_timedacquire(apr_proc_mutex_t *mutex, apr_time_t timeout, int absolute) { -#if !HAVE_SEM_TIMEDWAIT -extern int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); -#endif if (timeout < 0) { return proc_mutex_posix_acquire(mutex); } @@ -216,6 +264,7 @@ extern int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); mutex->curr_locked = 1; return APR_SUCCESS; } +#endif static apr_status_t proc_mutex_posix_release(apr_proc_mutex_t *mutex) { @@ -238,7 +287,11 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_posixsem_methods = proc_mutex_posix_create, proc_mutex_posix_acquire, proc_mutex_posix_tryacquire, +#if defined(HAVE_SEM_TIMEDWAIT) proc_mutex_posix_timedacquire, +#else + proc_mutex_spinsleep_timedacquire, +#endif proc_mutex_posix_release, proc_mutex_posix_cleanup, proc_mutex_no_child_init, @@ -337,28 +390,28 @@ static apr_status_t proc_mutex_sysv_tryacquire(apr_proc_mutex_t *mutex) return APR_SUCCESS; } +#if defined(HAVE_SEMTIMEDOP) static apr_status_t proc_mutex_sysv_timedacquire(apr_proc_mutex_t *mutex, apr_time_t timeout, int absolute) { -#if !HAVE_SEMTIMEDOP -extern int semtimedop(int semid, struct sembuf *sops, unsigned nsops, - const struct timespec *timeout); -#endif if (timeout < 0) { return proc_mutex_sysv_acquire(mutex); } else { int rc; - struct timespec abstime; - if (!absolute) { - timeout += apr_time_now(); + struct timespec reltime; + if (absolute) { + timeout -= apr_time_now(); + if (timeout < 0) { + return proc_mutex_sysv_tryacquire(mutex); + } } - abstime.tv_sec = apr_time_sec(timeout); - abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */ + reltime.tv_sec = apr_time_sec(timeout); + reltime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */ do { rc = semtimedop(mutex->os.crossproc, &proc_mutex_op_on, 1, - &abstime); + &reltime); } while (rc < 0 && errno == EINTR); if (rc < 0) { if (errno == EAGAIN) { @@ -370,6 +423,7 @@ extern int semtimedop(int semid, struct sembuf *sops, unsigned nsops, mutex->curr_locked = 1; return APR_SUCCESS; } +#endif static apr_status_t proc_mutex_sysv_release(apr_proc_mutex_t *mutex) { @@ -413,7 +467,11 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_sysv_methods = proc_mutex_sysv_create, proc_mutex_sysv_acquire, proc_mutex_sysv_tryacquire, +#if defined(HAVE_SEMTIMEDOP) proc_mutex_sysv_timedacquire, +#else + proc_mutex_spinsleep_timedacquire, +#endif proc_mutex_sysv_release, proc_mutex_sysv_cleanup, proc_mutex_no_child_init, @@ -426,6 +484,12 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_sysv_methods = #if APR_HAS_PROC_PTHREAD_SERIALIZE +#ifndef APR_USE_PROC_PTHREAD_MUTEX_COND +#define APR_USE_PROC_PTHREAD_MUTEX_COND \ + (defined(HAVE_PTHREAD_CONDATTR_SETPSHARED) \ + && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)) +#endif + /* The mmap()ed pthread_interproc is the native pthread_mutex_t followed * by a refcounter to track children using it. We want to avoid calling * pthread_mutex_destroy() on the shared mutex area while it is in use by @@ -436,12 +500,31 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_sysv_methods = * destroy it. */ typedef struct { +#define proc_pthread_cast(m) \ + ((proc_pthread_mutex_t *)(m)->os.pthread_interproc) pthread_mutex_t mutex; +#define proc_pthread_mutex(m) \ + (proc_pthread_cast(m)->mutex) +#if APR_USE_PROC_PTHREAD_MUTEX_COND + pthread_cond_t cond; +#define proc_pthread_mutex_cond(m) \ + (proc_pthread_cast(m)->cond) + apr_int32_t cond_locked; +#define proc_pthread_mutex_cond_locked(m) \ + ((m)->pthread_refcounting ? proc_pthread_cast(m)->cond_locked : -1) + apr_uint32_t cond_num_waiters; +#define proc_pthread_mutex_cond_num_waiters(m) \ + (proc_pthread_cast(m)->cond_num_waiters) +#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */ apr_uint32_t refcount; +#define proc_pthread_mutex_refcount(m) \ + (proc_pthread_cast(m)->refcount) } proc_pthread_mutex_t; -#define proc_pthread_mutex_refcount(m) \ - (((proc_pthread_mutex_t *)(m)->os.pthread_interproc)->refcount) + +static apr_status_t proc_mutex_pthread_timedacquire(apr_proc_mutex_t *mutex, + apr_time_t timeout, + int absolute); static APR_INLINE int proc_pthread_mutex_inc(apr_proc_mutex_t *mutex) { @@ -465,8 +548,14 @@ static apr_status_t proc_pthread_mutex_unref(void *mutex_) apr_proc_mutex_t *mutex=mutex_; apr_status_t rv; +#if APR_USE_PROC_PTHREAD_MUTEX_COND + if (proc_pthread_mutex_cond_locked(mutex) != -1) { + mutex->curr_locked = 0; + } + else +#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */ if (mutex->curr_locked == 1) { - if ((rv = pthread_mutex_unlock(mutex->os.pthread_interproc))) { + if ((rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)))) { #ifdef HAVE_ZOS_PTHREADS rv = errno; #endif @@ -474,7 +563,17 @@ static apr_status_t proc_pthread_mutex_unref(void *mutex_) } } if (!proc_pthread_mutex_dec(mutex)) { - if ((rv = pthread_mutex_destroy(mutex->os.pthread_interproc))) { +#if APR_USE_PROC_PTHREAD_MUTEX_COND + if (proc_pthread_mutex_cond_locked(mutex) != -1 && + (rv = pthread_cond_destroy(&proc_pthread_mutex_cond(mutex)))) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + return rv; + } +#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */ + + if ((rv = pthread_mutex_destroy(&proc_pthread_mutex(mutex)))) { #ifdef HAVE_ZOS_PTHREADS rv = errno; #endif @@ -526,6 +625,9 @@ static apr_status_t proc_mutex_pthread_create(apr_proc_mutex_t *new_mutex, new_mutex->pthread_refcounting = 1; new_mutex->curr_locked = -1; /* until the mutex has been created */ +#if APR_USE_PROC_PTHREAD_MUTEX_COND + proc_pthread_mutex_cond_locked(new_mutex) = -1; +#endif if ((rv = pthread_mutexattr_init(&mattr))) { #ifdef HAVE_ZOS_PTHREADS @@ -563,7 +665,7 @@ static apr_status_t proc_mutex_pthread_create(apr_proc_mutex_t *new_mutex, } #endif /* HAVE_PTHREAD_MUTEX_ROBUST */ - if ((rv = pthread_mutex_init(new_mutex->os.pthread_interproc, &mattr))) { + if ((rv = pthread_mutex_init(&proc_pthread_mutex(new_mutex), &mattr))) { #ifdef HAVE_ZOS_PTHREADS rv = errno; #endif @@ -604,105 +706,200 @@ static apr_status_t proc_mutex_pthread_child_init(apr_proc_mutex_t **mutex, static apr_status_t proc_mutex_pthread_acquire(apr_proc_mutex_t *mutex) { - apr_status_t rv; - - if ((rv = pthread_mutex_lock(mutex->os.pthread_interproc))) { -#ifdef HAVE_ZOS_PTHREADS - rv = errno; -#endif -#ifdef HAVE_PTHREAD_MUTEX_ROBUST - /* Okay, our owner died. Let's try to make it consistent again. */ - if (rv == EOWNERDEAD) { - proc_pthread_mutex_dec(mutex); - pthread_mutex_consistent_np(mutex->os.pthread_interproc); - } - else -#endif - return rv; - } - mutex->curr_locked = 1; - return APR_SUCCESS; + return proc_mutex_pthread_timedacquire(mutex, -1, 0); } static apr_status_t proc_mutex_pthread_tryacquire(apr_proc_mutex_t *mutex) { - apr_status_t rv; - - if ((rv = pthread_mutex_trylock(mutex->os.pthread_interproc))) { -#ifdef HAVE_ZOS_PTHREADS - rv = errno; -#endif - if (rv == EBUSY) { - return APR_EBUSY; - } -#ifdef HAVE_PTHREAD_MUTEX_ROBUST - /* Okay, our owner died. Let's try to make it consistent again. */ - if (rv == EOWNERDEAD) { - proc_pthread_mutex_dec(mutex); - pthread_mutex_consistent_np(mutex->os.pthread_interproc); - } - else -#endif - return rv; - } - mutex->curr_locked = 1; - return APR_SUCCESS; + apr_status_t rv = proc_mutex_pthread_timedacquire(mutex, 0, 0); + return (rv == APR_TIMEUP) ? APR_EBUSY : rv; } static apr_status_t proc_mutex_pthread_timedacquire(apr_proc_mutex_t *mutex, apr_time_t timeout, int absolute) { -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -extern int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout); +#if !APR_USE_PROC_PTHREAD_MUTEX_COND && !defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) + return proc_mutex_spinsleep_timedacquire(mutex, timeout, absolute); +#else + apr_status_t rv; + +#if APR_USE_PROC_PTHREAD_MUTEX_COND + if (proc_pthread_mutex_cond_locked(mutex) != -1) { + if ((rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)))) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; #endif - if (timeout < 0) { - return proc_mutex_pthread_acquire(mutex); - } - else { - apr_status_t rv; - struct timespec abstime; +#ifdef HAVE_PTHREAD_MUTEX_ROBUST + /* Okay, our owner died. Let's try to make it consistent again. */ + if (rv == EOWNERDEAD) { + proc_pthread_mutex_dec(mutex); + pthread_mutex_consistent_np(&proc_pthread_mutex(mutex)); + } + else +#endif + return rv; + } - if (!absolute) { - timeout += apr_time_now(); + if (!proc_pthread_mutex_cond_locked(mutex)) { + proc_pthread_mutex_cond_locked(mutex) = 1; + } + else if (!timeout) { + rv = APR_TIMEUP; + } + else { + proc_pthread_mutex_cond_num_waiters(mutex)++; + if (timeout < 0) { + rv = pthread_cond_wait(&proc_pthread_mutex_cond(mutex), + &proc_pthread_mutex(mutex)); +#ifdef HAVE_ZOS_PTHREADS + if (rv) { + rv = errno; + } +#endif + } + else { + struct timespec abstime; + if (!absolute) { + timeout += apr_time_now(); + } + abstime.tv_sec = apr_time_sec(timeout); + abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */ + rv = pthread_cond_timedwait(&proc_pthread_mutex_cond(mutex), + &proc_pthread_mutex(mutex), + &abstime); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + if (rv == ETIMEDOUT) { + rv = APR_TIMEUP; + } + } + } + proc_pthread_mutex_cond_num_waiters(mutex)--; + } + if (rv) { + pthread_mutex_unlock(&proc_pthread_mutex(mutex)); + return rv; } - abstime.tv_sec = apr_time_sec(timeout); - abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */ - if ((rv = pthread_mutex_timedlock(mutex->os.pthread_interproc, - &abstime))) { -#ifdef HAVE_ZOS_PTHREADS + rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS rv = errno; #endif - if (rv == ETIMEDOUT) { - return APR_TIMEUP; + return rv; + } + } + else +#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */ + { + if (timeout < 0) { + rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + } + } + else if (!timeout) { + rv = pthread_mutex_trylock(&proc_pthread_mutex(mutex)); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + if (rv == EBUSY) { + return APR_TIMEUP; + } + } + } + else { + struct timespec abstime; + if (!absolute) { + timeout += apr_time_now(); + } + abstime.tv_sec = apr_time_sec(timeout); + abstime.tv_nsec = apr_time_usec(timeout) * 1000; /* nanoseconds */ + rv = pthread_mutex_timedlock(&proc_pthread_mutex(mutex), &abstime); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + if (rv == ETIMEDOUT) { + return APR_TIMEUP; + } } + } + if (rv) { #ifdef HAVE_PTHREAD_MUTEX_ROBUST /* Okay, our owner died. Let's try to make it consistent again. */ if (rv == EOWNERDEAD) { proc_pthread_mutex_dec(mutex); - pthread_mutex_consistent_np(mutex->os.pthread_interproc); + pthread_mutex_consistent_np(&proc_pthread_mutex(mutex)); } else #endif return rv; } } + mutex->curr_locked = 1; return APR_SUCCESS; +#endif } static apr_status_t proc_mutex_pthread_release(apr_proc_mutex_t *mutex) { apr_status_t rv; +#if APR_USE_PROC_PTHREAD_MUTEX_COND + if (proc_pthread_mutex_cond_locked(mutex) != -1) { + if ((rv = pthread_mutex_lock(&proc_pthread_mutex(mutex)))) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif +#ifdef HAVE_PTHREAD_MUTEX_ROBUST + /* Okay, our owner died. Let's try to make it consistent again. */ + if (rv == EOWNERDEAD) { + proc_pthread_mutex_dec(mutex); + pthread_mutex_consistent_np(&proc_pthread_mutex(mutex)); + } + else +#endif + return rv; + } + + if (!proc_pthread_mutex_cond_locked(mutex)) { + rv = APR_EINVAL; + } + else if (proc_pthread_mutex_cond_num_waiters(mutex)) { + rv = pthread_cond_signal(&proc_pthread_mutex_cond(mutex)); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + } + } + else { + proc_pthread_mutex_cond_locked(mutex) = 0; + rv = APR_SUCCESS; + } + if (rv) { + pthread_mutex_unlock(&proc_pthread_mutex(mutex)); + return rv; + } + } +#endif /* APR_USE_PROC_PTHREAD_MUTEX_COND */ + mutex->curr_locked = 0; - if ((rv = pthread_mutex_unlock(mutex->os.pthread_interproc))) { + if ((rv = pthread_mutex_unlock(&proc_pthread_mutex(mutex)))) { #ifdef HAVE_ZOS_PTHREADS rv = errno; #endif return rv; } + return APR_SUCCESS; } @@ -721,6 +918,69 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_methods = "pthread" }; +#if APR_USE_PROC_PTHREAD_MUTEX_COND +static apr_status_t proc_mutex_pthread_cond_create(apr_proc_mutex_t *new_mutex, + const char *fname) +{ + apr_status_t rv; + pthread_condattr_t cattr; + + rv = proc_mutex_pthread_create(new_mutex, fname); + if (rv != APR_SUCCESS) { + return rv; + } + + if ((rv = pthread_condattr_init(&cattr))) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + apr_pool_cleanup_run(new_mutex->pool, new_mutex, + apr_proc_mutex_cleanup); + return rv; + } + if ((rv = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED))) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + pthread_condattr_destroy(&cattr); + apr_pool_cleanup_run(new_mutex->pool, new_mutex, + apr_proc_mutex_cleanup); + return rv; + } + if ((rv = pthread_cond_init(&proc_pthread_mutex_cond(new_mutex), + &cattr))) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + pthread_condattr_destroy(&cattr); + apr_pool_cleanup_run(new_mutex->pool, new_mutex, + apr_proc_mutex_cleanup); + return rv; + } + pthread_condattr_destroy(&cattr); + + proc_pthread_mutex_cond_locked(new_mutex) = 0; + proc_pthread_mutex_cond_num_waiters(new_mutex) = 0; + + return APR_SUCCESS; +} + +static const apr_proc_mutex_unix_lock_methods_t mutex_proc_pthread_cond_methods = +{ + APR_PROCESS_LOCK_MECH_IS_GLOBAL, + proc_mutex_pthread_cond_create, + proc_mutex_pthread_acquire, + proc_mutex_pthread_tryacquire, + proc_mutex_pthread_timedacquire, + proc_mutex_pthread_release, + proc_mutex_pthread_cleanup, + proc_mutex_pthread_child_init, + proc_mutex_no_perms_set, + APR_LOCK_PROC_PTHREAD, + "pthread" +}; +#endif + #endif #if APR_HAS_FCNTL_SERIALIZE @@ -836,13 +1096,6 @@ static apr_status_t proc_mutex_fcntl_tryacquire(apr_proc_mutex_t *mutex) return APR_SUCCESS; } -static apr_status_t proc_mutex_fcntl_timedacquire(apr_proc_mutex_t *mutex, - apr_time_t timeout, - int absolute) -{ - return APR_ENOTIMPL; -} - static apr_status_t proc_mutex_fcntl_release(apr_proc_mutex_t *mutex) { int rc; @@ -883,7 +1136,7 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_fcntl_methods = proc_mutex_fcntl_create, proc_mutex_fcntl_acquire, proc_mutex_fcntl_tryacquire, - proc_mutex_fcntl_timedacquire, + proc_mutex_spinsleep_timedacquire, proc_mutex_fcntl_release, proc_mutex_fcntl_cleanup, proc_mutex_no_child_init, @@ -987,13 +1240,6 @@ static apr_status_t proc_mutex_flock_tryacquire(apr_proc_mutex_t *mutex) return APR_SUCCESS; } -static apr_status_t proc_mutex_flock_timedacquire(apr_proc_mutex_t *mutex, - apr_time_t timeout, - int absolute) -{ - return APR_ENOTIMPL; -} - static apr_status_t proc_mutex_flock_release(apr_proc_mutex_t *mutex) { int rc; @@ -1064,7 +1310,7 @@ static const apr_proc_mutex_unix_lock_methods_t mutex_flock_methods = proc_mutex_flock_create, proc_mutex_flock_acquire, proc_mutex_flock_tryacquire, - proc_mutex_flock_timedacquire, + proc_mutex_spinsleep_timedacquire, proc_mutex_flock_release, proc_mutex_flock_cleanup, proc_mutex_flock_child_init, @@ -1171,6 +1417,43 @@ static apr_status_t proc_mutex_choose_method(apr_proc_mutex_t *new_mutex, return APR_ENOTIMPL; #endif break; + case APR_LOCK_DEFAULT_TIMED: +#if APR_HAS_PROC_PTHREAD_SERIALIZE \ + && (APR_USE_PROC_PTHREAD_MUTEX_COND \ + || defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK)) \ + && defined(HAVE_PTHREAD_MUTEX_ROBUST) +#if APR_USE_PROC_PTHREAD_MUTEX_COND + new_mutex->meth = &mutex_proc_pthread_cond_methods; +#else + new_mutex->meth = &mutex_proc_pthread_methods; +#endif + if (ospmutex) { + if (ospmutex->pthread_interproc == NULL) { + return APR_EINVAL; + } + new_mutex->os.pthread_interproc = ospmutex->pthread_interproc; + } + break; +#elif APR_HAS_SYSVSEM_SERIALIZE && defined(HAVE_SEMTIMEDOP) + new_mutex->meth = &mutex_sysv_methods; + if (ospmutex) { + if (ospmutex->crossproc == -1) { + return APR_EINVAL; + } + new_mutex->os.crossproc = ospmutex->crossproc; + } + break; +#elif APR_HAS_POSIXSEM_SERIALIZE && defined(HAVE_SEM_TIMEDWAIT) + new_mutex->meth = &mutex_posixsem_methods; + if (ospmutex) { + if (ospmutex->psem_interproc == NULL) { + return APR_EINVAL; + } + new_mutex->os.psem_interproc = ospmutex->psem_interproc; + } + break; +#endif + /* fall trough */ case APR_LOCK_DEFAULT: #if APR_USE_FLOCK_SERIALIZE new_mutex->meth = &mutex_flock_methods; @@ -1216,18 +1499,6 @@ static apr_status_t proc_mutex_choose_method(apr_proc_mutex_t *new_mutex, return APR_ENOTIMPL; #endif break; - case APR_LOCK_DEFAULT_TIMED: -#if APR_HAS_PROC_PTHREAD_SERIALIZE \ - && defined(HAVE_PTHREAD_MUTEX_ROBUST) - new_mutex->meth = &mutex_proc_pthread_methods; -#elif APR_HAS_SYSVSEM_SERIALIZE - new_mutex->meth = &mutex_sysv_methods; -#elif APR_HAS_POSIXSEM_SERIALIZE - new_mutex->meth = &mutex_posixsem_methods; -#else - return APR_ENOTIMPL; -#endif - break; default: return APR_ENOTIMPL; } diff --git a/locks/unix/thread_mutex.c b/locks/unix/thread_mutex.c index 5c6716658..fe515b2e6 100644 --- a/locks/unix/thread_mutex.c +++ b/locks/unix/thread_mutex.c @@ -77,6 +77,19 @@ APR_DECLARE(apr_status_t) apr_thread_mutex_create(apr_thread_mutex_t **mutex, return rv; } +#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK + if (flags & APR_THREAD_MUTEX_TIMED) { + rv = apr_thread_cond_create(&new_mutex->cond, pool); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + pthread_mutex_destroy(&new_mutex->mutex); + return rv; + } + } +#endif + apr_pool_cleanup_register(new_mutex->pool, new_mutex, thread_mutex_cleanup, apr_pool_cleanup_null); @@ -182,10 +195,7 @@ APR_DECLARE(apr_status_t) apr_thread_mutex_timedlock(apr_thread_mutex_t *mutex, { apr_status_t rv = APR_ENOTIMPL; -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -extern int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout); -#endif - +#ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK if (timeout < 0) { rv = pthread_mutex_lock(&mutex->mutex); if (rv) { @@ -213,6 +223,55 @@ extern int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec } } } + +#else /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */ + + if (mutex->cond) { + apr_status_t rv2; + + rv = pthread_mutex_lock(&mutex->mutex); + if (rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + return rv; + } + + if (mutex->locked) { + mutex->num_waiters++; + if (timeout < 0) { + rv = apr_thread_cond_wait(mutex->cond, mutex); + } + else { + if (absolute) { + apr_time_t now = apr_time_now(); + if (timeout > now) { + timeout -= now; + } + else { + timeout = 0; + } + } + rv = apr_thread_cond_timedwait(mutex->cond, mutex, timeout); + } + mutex->num_waiters--; + } + else { + mutex->locked = 1; + } + + rv2 = pthread_mutex_unlock(&mutex->mutex); + if (rv2 && !rv) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#else + rv = rv2; +#endif + } + } + +#endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */ + return rv; } diff --git a/test/testprocmutex.c b/test/testprocmutex.c index 599d58520..3aff25b88 100644 --- a/test/testprocmutex.c +++ b/test/testprocmutex.c @@ -20,6 +20,7 @@ #include "apr_proc_mutex.h" #include "apr_errno.h" #include "apr_general.h" +#include "apr_strings.h" #include "apr_getopt.h" #include <stdio.h> #include <stdlib.h> @@ -155,6 +156,19 @@ static void test_exclusive(abts_case *tc, const char *lockname, else { APR_ASSERT_SUCCESS(tc, "check for trylock", rv); + for (n = 0; n < 2; n++) { + rv = apr_proc_mutex_trylock(proc_lock); + /* Some mech (eg. flock or fcntl) may succeed when the + * lock is re-acquired in the same process. + */ + if (rv != APR_SUCCESS) { + ABTS_ASSERT(tc, + apr_psprintf(p, "%s_trylock() should be busy => %pm", + mech->name, &rv), + APR_STATUS_IS_EBUSY(rv)); + } + } + rv = apr_proc_mutex_unlock(proc_lock); APR_ASSERT_SUCCESS(tc, "unlock after trylock check", rv); @@ -179,6 +193,19 @@ static void test_exclusive(abts_case *tc, const char *lockname, else { APR_ASSERT_SUCCESS(tc, "check for timedlock", rv); + for (n = 0; n < 2; n++) { + rv = apr_proc_mutex_timedlock(proc_lock, 1, 0); + /* Some mech (eg. flock or fcntl) may succeed when the + * lock is re-acquired in the same process. + */ + if (rv != APR_SUCCESS) { + ABTS_ASSERT(tc, + apr_psprintf(p, "%s_timedlock() should time out => %pm", + mech->name, &rv), + APR_STATUS_IS_TIMEUP(rv)); + } + } + rv = apr_proc_mutex_unlock(proc_lock); APR_ASSERT_SUCCESS(tc, "unlock after timedlock check", rv); |