diff options
author | unknown <tim@threads.polyesthetic.msg> | 2001-03-04 19:42:05 -0500 |
---|---|---|
committer | unknown <tim@threads.polyesthetic.msg> | 2001-03-04 19:42:05 -0500 |
commit | 07dc15a5b0fafaf0a0bcde2768b34aad2f3825fa (patch) | |
tree | 9dd732e08dba156ee3d7635caedc0dc3107ecac6 /bdb/mutex | |
parent | 542e1c18dc5bf80665df55ffa04a48d986945259 (diff) | |
download | mariadb-git-07dc15a5b0fafaf0a0bcde2768b34aad2f3825fa.tar.gz |
Import changeset
Diffstat (limited to 'bdb/mutex')
-rw-r--r-- | bdb/mutex/README | 108 | ||||
-rw-r--r-- | bdb/mutex/mut_fcntl.c | 174 | ||||
-rw-r--r-- | bdb/mutex/mut_pthread.c | 328 | ||||
-rw-r--r-- | bdb/mutex/mut_tas.c | 200 | ||||
-rw-r--r-- | bdb/mutex/mutex.c | 253 | ||||
-rw-r--r-- | bdb/mutex/uts4_cc.s | 21 |
6 files changed, 1084 insertions, 0 deletions
diff --git a/bdb/mutex/README b/bdb/mutex/README new file mode 100644 index 00000000000..323c34f1e74 --- /dev/null +++ b/bdb/mutex/README @@ -0,0 +1,108 @@ +# $Id: README,v 11.2 1999/11/21 18:12:48 bostic Exp $ + +Note: this only applies to locking using test-and-set and fcntl calls, +pthreads were added after this was written. + +Resource locking routines: lock based on a db_mutex_t. All this gunk +(including trying to make assembly code portable), is necessary because +System V semaphores require system calls for uncontested locks and we +don't want to make two system calls per resource lock. + +First, this is how it works. The db_mutex_t structure contains a resource +test-and-set lock (tsl), a file offset, a pid for debugging and statistics +information. + +If HAVE_MUTEX_THREADS is defined (i.e. we know how to do test-and-sets +for this compiler/architecture combination), we try and lock the resource +tsl __os_spin() times. If we can't acquire the lock that way, we use a +system call to sleep for 1ms, 2ms, 4ms, etc. (The time is bounded at 1 +second, just in case.) Using the timer backoff means that there are two +assumptions: that locks are held for brief periods (never over system +calls or I/O) and that locks are not hotly contested. + +If HAVE_MUTEX_THREADS is not defined, i.e. we can't do test-and-sets, we +use a file descriptor to do byte locking on a file at a specified offset. +In this case, ALL of the locking is done in the kernel. Because file +descriptors are allocated per process, we have to provide the file +descriptor as part of the lock call. We still have to do timer backoff +because we need to be able to block ourselves, i.e. the lock manager +causes processes to wait by having the process acquire a mutex and then +attempting to re-acquire the mutex. There's no way to use kernel locking +to block yourself, i.e. if you hold a lock and attempt to re-acquire it, +the attempt will succeed. + +Next, let's talk about why it doesn't work the way a reasonable person +would think it should work. + +Ideally, we'd have the ability to try to lock the resource tsl, and if +that fails, increment a counter of waiting processes, then block in the +kernel until the tsl is released. The process holding the resource tsl +would see the wait counter when it went to release the resource tsl, and +would wake any waiting processes up after releasing the lock. This would +actually require both another tsl (call it the mutex tsl) and +synchronization between the call that blocks in the kernel and the actual +resource tsl. The mutex tsl would be used to protect accesses to the +db_mutex_t itself. Locking the mutex tsl would be done by a busy loop, +which is safe because processes would never block holding that tsl (all +they would do is try to obtain the resource tsl and set/check the wait +count). The problem in this model is that the blocking call into the +kernel requires a blocking semaphore, i.e. one whose normal state is +locked. + +The only portable forms of locking under UNIX are fcntl(2) on a file +descriptor/offset, and System V semaphores. Neither of these locking +methods are sufficient to solve the problem. + +The problem with fcntl locking is that only the process that obtained the +lock can release it. Remember, we want the normal state of the kernel +semaphore to be locked. So, if the creator of the db_mutex_t were to +initialize the lock to "locked", then a second process locks the resource +tsl, and then a third process needs to block, waiting for the resource +tsl, when the second process wants to wake up the third process, it can't +because it's not the holder of the lock! For the second process to be +the holder of the lock, we would have to make a system call per +uncontested lock, which is what we were trying to get away from in the +first place. + +There are some hybrid schemes, such as signaling the holder of the lock, +or using a different blocking offset depending on which process is +holding the lock, but it gets complicated fairly quickly. I'm open to +suggestions, but I'm not holding my breath. + +Regardless, we use this form of locking when HAVE_SPINLOCKS is not +defined, (i.e. we're locking in the kernel) because it doesn't have the +limitations found in System V semaphores, and because the normal state of +the kernel object in that case is unlocked, so the process releasing the +lock is also the holder of the lock. + +The System V semaphore design has a number of other limitations that make +it inappropriate for this task. Namely: + +First, the semaphore key name space is separate from the file system name +space (although there exist methods for using file names to create +semaphore keys). If we use a well-known key, there's no reason to believe +that any particular key will not already be in use, either by another +instance of the DB application or some other application, in which case +the DB application will fail. If we create a key, then we have to use a +file system name to rendezvous and pass around the key. + +Second, System V semaphores traditionally have compile-time, system-wide +limits on the number of semaphore keys that you can have. Typically, that +number is far too low for any practical purpose. Since the semaphores +permit more than a single slot per semaphore key, we could try and get +around that limit by using multiple slots, but that means that the file +that we're using for rendezvous is going to have to contain slot +information as well as semaphore key information, and we're going to be +reading/writing it on every db_mutex_t init or destroy operation. Anyhow, +similar compile-time, system-wide limits on the numbers of slots per +semaphore key kick in, and you're right back where you started. + +My fantasy is that once POSIX.1 standard mutexes are in wide-spread use, +we can switch to them. My guess is that it won't happen, because the +POSIX semaphores are only required to work for threads within a process, +and not independent processes. + +Note: there are races in the statistics code, but since it's just that, +I didn't bother fixing them. (The fix requires a mutex tsl, so, when/if +this code is fixed to do rational locking (see above), then change the +statistics update code to acquire/release the mutex tsl. diff --git a/bdb/mutex/mut_fcntl.c b/bdb/mutex/mut_fcntl.c new file mode 100644 index 00000000000..02f4d4044f8 --- /dev/null +++ b/bdb/mutex/mut_fcntl.c @@ -0,0 +1,174 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: mut_fcntl.c,v 11.11 2001/01/11 18:19:53 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "db_int.h" + +/* + * __db_fcntl_mutex_init -- + * Initialize a DB mutex structure. + * + * PUBLIC: int __db_fcntl_mutex_init __P((DB_ENV *, MUTEX *, u_int32_t)); + */ +int +__db_fcntl_mutex_init(dbenv, mutexp, offset) + DB_ENV *dbenv; + MUTEX *mutexp; + u_int32_t offset; +{ + memset(mutexp, 0, sizeof(*mutexp)); + + /* + * This is where we decide to ignore locks we don't need to set -- if + * the application is private, we don't need any locks. + */ + if (F_ISSET(dbenv, DB_ENV_PRIVATE)) { + F_SET(mutexp, MUTEX_IGNORE); + return (0); + } + + mutexp->off = offset; +#ifdef MUTEX_SYSTEM_RESOURCES + mutexp->reg_off = INVALID_ROFF; +#endif + F_SET(mutexp, MUTEX_INITED); + + return (0); +} + +/* + * __db_fcntl_mutex_lock + * Lock on a mutex, blocking if necessary. + * + * PUBLIC: int __db_fcntl_mutex_lock __P((DB_ENV *, MUTEX *, DB_FH *)); + */ +int +__db_fcntl_mutex_lock(dbenv, mutexp, fhp) + DB_ENV *dbenv; + MUTEX *mutexp; + DB_FH *fhp; +{ + struct flock k_lock; + int locked, ms, waited; + + if (!dbenv->db_mutexlocks) + return (0); + + /* Initialize the lock. */ + k_lock.l_whence = SEEK_SET; + k_lock.l_start = mutexp->off; + k_lock.l_len = 1; + + for (locked = waited = 0;;) { + /* + * Wait for the lock to become available; wait 1ms initially, + * up to 1 second. + */ + for (ms = 1; mutexp->pid != 0;) { + waited = 1; + __os_yield(NULL, ms * USEC_PER_MS); + if ((ms <<= 1) > MS_PER_SEC) + ms = MS_PER_SEC; + } + + /* Acquire an exclusive kernel lock. */ + k_lock.l_type = F_WRLCK; + if (fcntl(fhp->fd, F_SETLKW, &k_lock)) + return (__os_get_errno()); + + /* If the resource is still available, it's ours. */ + if (mutexp->pid == 0) { + locked = 1; + mutexp->pid = (u_int32_t)getpid(); + } + + /* Release the kernel lock. */ + k_lock.l_type = F_UNLCK; + if (fcntl(fhp->fd, F_SETLK, &k_lock)) + return (__os_get_errno()); + + /* + * If we got the resource lock we're done. + * + * !!! + * We can't check to see if the lock is ours, because we may + * be trying to block ourselves in the lock manager, and so + * the holder of the lock that's preventing us from getting + * the lock may be us! (Seriously.) + */ + if (locked) + break; + } + + if (waited) + ++mutexp->mutex_set_wait; + else + ++mutexp->mutex_set_nowait; + return (0); +} + +/* + * __db_fcntl_mutex_unlock -- + * Release a lock. + * + * PUBLIC: int __db_fcntl_mutex_unlock __P((DB_ENV *, MUTEX *)); + */ +int +__db_fcntl_mutex_unlock(dbenv, mutexp) + DB_ENV *dbenv; + MUTEX *mutexp; +{ + if (!dbenv->db_mutexlocks) + return (0); + +#ifdef DIAGNOSTIC +#define MSG "mutex_unlock: ERROR: released lock that was unlocked\n" +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + if (mutexp->pid == 0) + write(STDERR_FILENO, MSG, sizeof(MSG) - 1); +#endif + + /* + * Release the resource. We don't have to acquire any locks because + * processes trying to acquire the lock are checking for a pid set to + * 0/non-0, not to any specific value. + */ + mutexp->pid = 0; + + return (0); +} + +/* + * __db_fcntl_mutex_destroy -- + * Destroy a MUTEX. + * + * PUBLIC: int __db_fcntl_mutex_destroy __P((MUTEX *)); + */ +int +__db_fcntl_mutex_destroy(mutexp) + MUTEX *mutexp; +{ + COMPQUIET(mutexp, NULL); + + return (0); +} diff --git a/bdb/mutex/mut_pthread.c b/bdb/mutex/mut_pthread.c new file mode 100644 index 00000000000..3de4abcefc5 --- /dev/null +++ b/bdb/mutex/mut_pthread.c @@ -0,0 +1,328 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: mut_pthread.c,v 11.33 2001/01/09 00:56:16 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#include <unistd.h> +#endif + +#include "db_int.h" + +#ifdef DIAGNOSTIC +#undef MSG1 +#define MSG1 "mutex_lock: ERROR: lock currently in use: pid: %lu.\n" +#undef MSG2 +#define MSG2 "mutex_unlock: ERROR: lock already unlocked\n" +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif +#endif + +#ifdef HAVE_MUTEX_SOLARIS_LWP +#define pthread_cond_signal _lwp_cond_signal +#define pthread_cond_wait _lwp_cond_wait +#define pthread_mutex_lock _lwp_mutex_lock +#define pthread_mutex_trylock _lwp_mutex_trylock +#define pthread_mutex_unlock _lwp_mutex_unlock +#define pthread_self _lwp_self +#define pthread_mutex_destroy(x) 0 +#endif +#ifdef HAVE_MUTEX_UI_THREADS +#define pthread_cond_signal cond_signal +#define pthread_cond_wait cond_wait +#define pthread_mutex_lock mutex_lock +#define pthread_mutex_trylock mutex_trylock +#define pthread_mutex_unlock mutex_unlock +#define pthread_self thr_self +#define pthread_mutex_destroy mutex_destroy +#endif + +#define PTHREAD_UNLOCK_ATTEMPTS 5 + +/* + * __db_pthread_mutex_init -- + * Initialize a MUTEX. + * + * PUBLIC: int __db_pthread_mutex_init __P((DB_ENV *, MUTEX *, u_int32_t)); + */ +int +__db_pthread_mutex_init(dbenv, mutexp, flags) + DB_ENV *dbenv; + MUTEX *mutexp; + u_int32_t flags; +{ + int ret; + + ret = 0; + memset(mutexp, 0, sizeof(*mutexp)); + + /* + * If this is a thread lock or the process has told us that there are + * no other processes in the environment, use thread-only locks, they + * are faster in some cases. + * + * This is where we decide to ignore locks we don't need to set -- if + * the application isn't threaded, there aren't any threads to block. + */ + if (LF_ISSET(MUTEX_THREAD) || F_ISSET(dbenv, DB_ENV_PRIVATE)) { + if (!F_ISSET(dbenv, DB_ENV_THREAD)) { + F_SET(mutexp, MUTEX_IGNORE); + return (0); + } + F_SET(mutexp, MUTEX_THREAD); + } + +#ifdef HAVE_MUTEX_PTHREADS + { + pthread_condattr_t condattr, *condattrp = NULL; + pthread_mutexattr_t mutexattr, *mutexattrp = NULL; + + if (!F_ISSET(mutexp, MUTEX_THREAD)) { + ret = pthread_condattr_init(&condattr); + if (ret == 0) + ret = pthread_condattr_setpshared( + &condattr, PTHREAD_PROCESS_SHARED); + condattrp = &condattr; + + if (ret == 0) + ret = pthread_mutexattr_init(&mutexattr); + if (ret == 0) + ret = pthread_mutexattr_setpshared( + &mutexattr, PTHREAD_PROCESS_SHARED); + mutexattrp = &mutexattr; + } + + if (ret == 0) + ret = pthread_mutex_init(&mutexp->mutex, mutexattrp); + if (mutexattrp != NULL) + pthread_mutexattr_destroy(mutexattrp); + if (LF_ISSET(MUTEX_SELF_BLOCK)) { + if (ret == 0) + ret = pthread_cond_init(&mutexp->cond, condattrp); + + F_SET(mutexp, MUTEX_SELF_BLOCK); + if (condattrp != NULL) + pthread_condattr_destroy(condattrp); + }} +#endif +#ifdef HAVE_MUTEX_SOLARIS_LWP + /* + * XXX + * Gcc complains about missing braces in the static initializations of + * lwp_cond_t and lwp_mutex_t structures because the structures contain + * sub-structures/unions and the Solaris include file that defines the + * initialization values doesn't have surrounding braces. There's not + * much we can do. + */ + if (F_ISSET(mutexp, MUTEX_THREAD)) { + static lwp_mutex_t mi = DEFAULTMUTEX; + + mutexp->mutex = mi; + } else { + static lwp_mutex_t mi = SHAREDMUTEX; + + mutexp->mutex = mi; + } + if (LF_ISSET(MUTEX_SELF_BLOCK)) { + if (F_ISSET(mutexp, MUTEX_THREAD)) { + static lwp_cond_t ci = DEFAULTCV; + + mutexp->cond = ci; + } else { + static lwp_cond_t ci = SHAREDCV; + + mutexp->cond = ci; + } + F_SET(mutexp, MUTEX_SELF_BLOCK); + } +#endif +#ifdef HAVE_MUTEX_UI_THREADS + { + int type; + + type = F_ISSET(mutexp, MUTEX_THREAD) ? USYNC_THREAD : USYNC_PROCESS; + + ret = mutex_init(&mutexp->mutex, type, NULL); + if (ret == 0 && LF_ISSET(MUTEX_SELF_BLOCK)) { + ret = cond_init(&mutexp->cond, type, NULL); + + F_SET(mutexp, MUTEX_SELF_BLOCK); + }} +#endif + + mutexp->spins = __os_spin(); +#ifdef MUTEX_SYSTEM_RESOURCES + mutexp->reg_off = INVALID_ROFF; +#endif + if (ret == 0) + F_SET(mutexp, MUTEX_INITED); + + return (ret); +} + +/* + * __db_pthread_mutex_lock + * Lock on a mutex, logically blocking if necessary. + * + * PUBLIC: int __db_pthread_mutex_lock __P((DB_ENV *, MUTEX *)); + */ +int +__db_pthread_mutex_lock(dbenv, mutexp) + DB_ENV *dbenv; + MUTEX *mutexp; +{ + u_int32_t nspins; + int i, ret, waited; + + if (!dbenv->db_mutexlocks || F_ISSET(mutexp, MUTEX_IGNORE)) + return (0); + + /* Attempt to acquire the resource for N spins. */ + for (nspins = mutexp->spins; nspins > 0; --nspins) + if (pthread_mutex_trylock(&mutexp->mutex) == 0) + break; + + if (nspins == 0 && (ret = pthread_mutex_lock(&mutexp->mutex)) != 0) + return (ret); + + if (F_ISSET(mutexp, MUTEX_SELF_BLOCK)) { + for (waited = 0; mutexp->locked != 0; waited = 1) { + ret = pthread_cond_wait(&mutexp->cond, &mutexp->mutex); + /* + * !!! + * Solaris bug workaround: + * pthread_cond_wait() sometimes returns ETIME -- out + * of sheer paranoia, check both ETIME and ETIMEDOUT. + * We believe this happens when the application uses + * SIGALRM for some purpose, e.g., the C library sleep + * call, and Solaris delivers the signal to the wrong + * LWP. + */ + if (ret != 0 && ret != ETIME && ret != ETIMEDOUT) + return (ret); + } + + if (waited) + ++mutexp->mutex_set_wait; + else + ++mutexp->mutex_set_nowait; + +#ifdef DIAGNOSTIC + mutexp->locked = (u_int32_t)pthread_self(); +#else + mutexp->locked = 1; +#endif + /* + * According to HP-UX engineers contacted by Netscape, + * pthread_mutex_unlock() will occasionally return EFAULT + * for no good reason on mutexes in shared memory regions, + * and the correct caller behavior is to try again. Do + * so, up to PTHREAD_UNLOCK_ATTEMPTS consecutive times. + * Note that we don't bother to restrict this to HP-UX; + * it should be harmless elsewhere. [#2471] + */ + i = PTHREAD_UNLOCK_ATTEMPTS; + do { + ret = pthread_mutex_unlock(&mutexp->mutex); + } while (ret == EFAULT && --i > 0); + if (ret != 0) + return (ret); + } else { + if (nspins == mutexp->spins) + ++mutexp->mutex_set_nowait; + else + ++mutexp->mutex_set_wait; +#ifdef DIAGNOSTIC + if (mutexp->locked) { + char msgbuf[128]; + (void)snprintf(msgbuf, + sizeof(msgbuf), MSG1, (u_long)mutexp->locked); + (void)write(STDERR_FILENO, msgbuf, strlen(msgbuf)); + } + mutexp->locked = (u_int32_t)pthread_self(); +#else + mutexp->locked = 1; +#endif + } + return (0); +} + +/* + * __db_pthread_mutex_unlock -- + * Release a lock. + * + * PUBLIC: int __db_pthread_mutex_unlock __P((DB_ENV *, MUTEX *)); + */ +int +__db_pthread_mutex_unlock(dbenv, mutexp) + DB_ENV *dbenv; + MUTEX *mutexp; +{ + int i, ret; + + if (!dbenv->db_mutexlocks || F_ISSET(mutexp, MUTEX_IGNORE)) + return (0); + +#ifdef DIAGNOSTIC + if (!mutexp->locked) + (void)write(STDERR_FILENO, MSG2, sizeof(MSG2) - 1); +#endif + + if (F_ISSET(mutexp, MUTEX_SELF_BLOCK)) { + if ((ret = pthread_mutex_lock(&mutexp->mutex)) != 0) + return (ret); + + mutexp->locked = 0; + + if ((ret = pthread_cond_signal(&mutexp->cond)) != 0) + return (ret); + + /* See comment above; workaround for [#2471]. */ + i = PTHREAD_UNLOCK_ATTEMPTS; + do { + ret = pthread_mutex_unlock(&mutexp->mutex); + } while (ret == EFAULT && --i > 0); + if (ret != 0) + return (ret); + } else { + mutexp->locked = 0; + + /* See comment above; workaround for [#2471]. */ + i = PTHREAD_UNLOCK_ATTEMPTS; + do { + ret = pthread_mutex_unlock(&mutexp->mutex); + } while (ret == EFAULT && --i > 0); + if (ret != 0) + return (ret); + } + + return (0); +} + +/* + * __db_pthread_mutex_destroy -- + * Destroy a MUTEX. + * + * PUBLIC: int __db_pthread_mutex_destroy __P((MUTEX *)); + */ +int +__db_pthread_mutex_destroy(mutexp) + MUTEX *mutexp; +{ + if (F_ISSET(mutexp, MUTEX_IGNORE)) + return (0); + + return (pthread_mutex_destroy(&mutexp->mutex)); +} diff --git a/bdb/mutex/mut_tas.c b/bdb/mutex/mut_tas.c new file mode 100644 index 00000000000..4b0db4bdf05 --- /dev/null +++ b/bdb/mutex/mut_tas.c @@ -0,0 +1,200 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: mut_tas.c,v 11.18 2000/11/30 00:58:41 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +/* + * This is where we load in the actual test-and-set mutex code. + */ +#define LOAD_ACTUAL_MUTEX_CODE +#include "db_int.h" + +#ifdef DIAGNOSTIC +#undef MSG1 +#define MSG1 "mutex_lock: ERROR: lock currently in use: pid: %lu.\n" +#undef MSG2 +#define MSG2 "mutex_unlock: ERROR: lock already unlocked\n" +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif +#endif + +/* + * __db_tas_mutex_init -- + * Initialize a MUTEX. + * + * PUBLIC: int __db_tas_mutex_init __P((DB_ENV *, MUTEX *, u_int32_t)); + */ +int +__db_tas_mutex_init(dbenv, mutexp, flags) + DB_ENV *dbenv; + MUTEX *mutexp; + u_int32_t flags; +{ + /* Check alignment. */ + DB_ASSERT(((db_alignp_t)mutexp & (MUTEX_ALIGN - 1)) == 0); + + memset(mutexp, 0, sizeof(*mutexp)); + + /* + * If this is a thread lock or the process has told us that there are + * no other processes in the environment, use thread-only locks, they + * are faster in some cases. + * + * This is where we decide to ignore locks we don't need to set -- if + * the application isn't threaded, there aren't any threads to block. + */ + if (LF_ISSET(MUTEX_THREAD) || F_ISSET(dbenv, DB_ENV_PRIVATE)) { + if (!F_ISSET(dbenv, DB_ENV_THREAD)) { + F_SET(mutexp, MUTEX_IGNORE); + return (0); + } + F_SET(mutexp, MUTEX_THREAD); + } + + /* Initialize the lock. */ + if (MUTEX_INIT(&mutexp->tas)) + return (__os_get_errno()); + + mutexp->spins = __os_spin(); +#ifdef MUTEX_SYSTEM_RESOURCES + mutexp->reg_off = INVALID_ROFF; +#endif + F_SET(mutexp, MUTEX_INITED); + + return (0); +} + +/* + * __db_tas_mutex_lock + * Lock on a mutex, logically blocking if necessary. + * + * PUBLIC: int __db_tas_mutex_lock __P((DB_ENV *, MUTEX *)); + */ +int +__db_tas_mutex_lock(dbenv, mutexp) + DB_ENV *dbenv; + MUTEX *mutexp; +{ + u_long ms; + int nspins; + + if (!dbenv->db_mutexlocks || F_ISSET(mutexp, MUTEX_IGNORE)) + return (0); + + ms = 1; + +loop: /* Attempt to acquire the resource for N spins. */ + for (nspins = mutexp->spins; nspins > 0; --nspins) { +#ifdef HAVE_MUTEX_HPPA_MSEM_INIT +relock: +#endif + if (!MUTEX_SET(&mutexp->tas)) + continue; +#ifdef HAVE_MUTEX_HPPA_MSEM_INIT + /* + * HP semaphores are unlocked automatically when a holding + * process exits. If the mutex appears to be locked + * (mutexp->locked != 0) but we got here, assume this has + * happened. Stick our own pid into mutexp->locked and + * lock again. (The default state of the mutexes used to + * block in __lock_get_internal is locked, so exiting with + * a locked mutex is reasonable behavior for a process that + * happened to initialize or use one of them.) + */ + if (mutexp->locked != 0) { + mutexp->locked = (u_int32_t)getpid(); + goto relock; + } + /* + * If we make it here, locked == 0, the diagnostic won't fire, + * and we were really unlocked by someone calling the + * DB mutex unlock function. + */ +#endif +#ifdef DIAGNOSTIC + if (mutexp->locked != 0) { + char msgbuf[128]; + (void)snprintf(msgbuf, + sizeof(msgbuf), MSG1, (u_long)mutexp->locked); + (void)write(STDERR_FILENO, msgbuf, strlen(msgbuf)); + } +#endif +#if defined(DIAGNOSTIC) || defined(HAVE_MUTEX_HPPA_MSEM_INIT) + mutexp->locked = (u_int32_t)getpid(); +#endif + if (ms == 1) + ++mutexp->mutex_set_nowait; + else + ++mutexp->mutex_set_wait; + return (0); + } + + /* Yield the processor; wait 1ms initially, up to 1 second. */ + __os_yield(NULL, ms * USEC_PER_MS); + if ((ms <<= 1) > MS_PER_SEC) + ms = MS_PER_SEC; + + goto loop; +} + +/* + * __db_tas_mutex_unlock -- + * Release a lock. + * + * PUBLIC: int __db_tas_mutex_unlock __P((DB_ENV *, MUTEX *)); + */ +int +__db_tas_mutex_unlock(dbenv, mutexp) + DB_ENV *dbenv; + MUTEX *mutexp; +{ + if (!dbenv->db_mutexlocks || F_ISSET(mutexp, MUTEX_IGNORE)) + return (0); + +#ifdef DIAGNOSTIC + if (!mutexp->locked) + (void)write(STDERR_FILENO, MSG2, sizeof(MSG2) - 1); +#endif +#if defined(DIAGNOSTIC) || defined(HAVE_MUTEX_HPPA_MSEM_INIT) + mutexp->locked = 0; +#endif + + MUTEX_UNSET(&mutexp->tas); + + return (0); +} + +/* + * __db_tas_mutex_destroy -- + * Destroy a MUTEX. + * + * PUBLIC: int __db_tas_mutex_destroy __P((MUTEX *)); + */ +int +__db_tas_mutex_destroy(mutexp) + MUTEX *mutexp; +{ + if (F_ISSET(mutexp, MUTEX_IGNORE)) + return (0); + + MUTEX_DESTROY(&mutexp->tas); + + return (0); +} diff --git a/bdb/mutex/mutex.c b/bdb/mutex/mutex.c new file mode 100644 index 00000000000..acc4af9bfcc --- /dev/null +++ b/bdb/mutex/mutex.c @@ -0,0 +1,253 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: mutex.c,v 11.14 2000/11/30 00:58:42 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#endif + +#include "db_int.h" + +/* + * __db_mutex_alloc -- + * Allocate and initialize a mutex. + * + * PUBLIC: int __db_mutex_alloc __P((DB_ENV *, REGINFO *, MUTEX **)); + */ +int +__db_mutex_alloc(dbenv, infop, storep) + DB_ENV *dbenv; + REGINFO *infop; + MUTEX **storep; +{ + int ret; + + /* + * If the architecture supports mutexes in heap memory, use that + * memory. If it doesn't, we have to allocate space in a region. + * + * XXX + * There's a nasty starvation issue here for applications running + * on systems that don't support mutexes in heap memory. If the + * normal state of the entire region is dirty (e.g., mpool), then + * we can run out of memory to allocate for mutexes when new files + * are opened in the pool. We're not trying to fix this for now, + * because the only known system where we can see this failure at + * the moment is HP-UX 10.XX. + */ +#ifdef MUTEX_NO_MALLOC_LOCKS + R_LOCK(dbenv, infop); + ret = __db_shalloc(infop->addr, sizeof(MUTEX), MUTEX_ALIGN, storep); + R_UNLOCK(dbenv, infop); +#else + COMPQUIET(dbenv, NULL); + COMPQUIET(infop, NULL); + ret = __os_calloc(dbenv, 1, sizeof(MUTEX), storep); +#endif + if (ret != 0) + __db_err(dbenv, "Unable to allocate memory for mutex"); + return (ret); +} + +/* + * __db_mutex_free -- + * Free a mutex. + * + * PUBLIC: void __db_mutex_free __P((DB_ENV *, REGINFO *, MUTEX *)); + */ +void +__db_mutex_free(dbenv, infop, mutexp) + DB_ENV *dbenv; + REGINFO *infop; + MUTEX *mutexp; +{ + if (F_ISSET(mutexp, MUTEX_INITED)) + __db_mutex_destroy(mutexp); + +#ifdef MUTEX_NO_MALLOC_LOCKS + R_LOCK(dbenv, infop); + __db_shalloc_free(infop->addr, mutexp); + R_UNLOCK(dbenv, infop); +#else + COMPQUIET(dbenv, NULL); + COMPQUIET(infop, NULL); + __os_free(mutexp, sizeof(*mutexp)); +#endif +} + +#ifdef MUTEX_SYSTEM_RESOURCES +/* + * __db_shreg_locks_record -- + * Record an entry in the shared locks area. + * Region lock must be held in caller. + * + * PUBLIC: int __db_shreg_locks_record __P((DB_ENV *, MUTEX *, REGINFO *, + * PUBLIC: REGMAINT *)); + */ +int +__db_shreg_locks_record(dbenv, mutexp, infop, rp) + DB_ENV *dbenv; + MUTEX *mutexp; + REGINFO *infop; + REGMAINT *rp; +{ + u_int i; + + if (!F_ISSET(mutexp, MUTEX_INITED)) + return (0); + DB_ASSERT(mutexp->reg_off == INVALID_ROFF); + rp->stat.st_records++; + i = (roff_t *)R_ADDR(infop, rp->regmutex_hint) - &rp->regmutexes[0]; + if (rp->regmutexes[i] != INVALID_ROFF) { + /* + * Our hint failed, search for a open slot. + */ + rp->stat.st_hint_miss++; + for (i = 0; i < rp->reglocks; i++) + if (rp->regmutexes[i] == INVALID_ROFF) + break; + if (i == rp->reglocks) { + rp->stat.st_max_locks++; + __db_err(dbenv, + "Region mutexes: Exceeded maximum lock slots %lu", + (u_long)rp->reglocks); + return (ENOMEM); + } + } else + rp->stat.st_hint_hit++; + /* + * When we get here, i is an empty slot. Record this + * mutex, set hint to point to the next slot and we are done. + */ + rp->regmutexes[i] = R_OFFSET(infop, mutexp); + mutexp->reg_off = R_OFFSET(infop, &rp->regmutexes[i]); + rp->regmutex_hint = (i < rp->reglocks - 1) ? + R_OFFSET(infop, &rp->regmutexes[i+1]) : + R_OFFSET(infop, &rp->regmutexes[0]); + return (0); +} + +/* + * __db_shreg_locks_clear -- + * Erase an entry in the shared locks area. + * Region lock must be held in caller. + * + * PUBLIC: void __db_shreg_locks_clear __P((MUTEX *, REGINFO *, REGMAINT *)); + */ +void +__db_shreg_locks_clear(mutexp, infop, rp) + MUTEX *mutexp; + REGINFO *infop; + REGMAINT *rp; +{ + if (!F_ISSET(mutexp, MUTEX_INITED)) + return; + /* + * This function is generally only called on a forcible + * remove of an environment. We recorded our index in + * the mutex. Find it and clear it. + */ + DB_ASSERT(mutexp->reg_off != INVALID_ROFF); + DB_ASSERT(*(roff_t *)R_ADDR(infop, mutexp->reg_off) == \ + R_OFFSET(infop, mutexp)); + *(roff_t *)R_ADDR(infop, mutexp->reg_off) = 0; + rp->regmutex_hint = mutexp->reg_off; + rp->stat.st_clears++; + mutexp->reg_off = INVALID_ROFF; + __db_mutex_destroy(mutexp); +} + +/* + * __db_shreg_locks_destroy -- + * Destroy all mutexes in a region's range. + * + * PUBLIC: void __db_shreg_locks_destroy __P((REGINFO *, REGMAINT *)); + */ +void +__db_shreg_locks_destroy(infop, rp) + REGINFO *infop; + REGMAINT *rp; +{ + u_int32_t i; + + /* + * Go through the list of all mutexes and destroy them. + */ + for (i = 0; i < rp->reglocks; i++) + if (rp->regmutexes[i] != 0) { + rp->stat.st_destroys++; + __db_mutex_destroy((MUTEX *)R_ADDR(infop, + rp->regmutexes[i])); + } +} + +/* + * __db_shreg_mutex_init -- + * Initialize a shared memory mutex. + * + * PUBLIC: int __db_shreg_mutex_init __P((DB_ENV *, MUTEX *, u_int32_t, + * PUBLIC: u_int32_t, REGINFO *, REGMAINT *)); + */ +int +__db_shreg_mutex_init(dbenv, mutexp, offset, flags, infop, rp) + DB_ENV *dbenv; + MUTEX *mutexp; + u_int32_t offset; + u_int32_t flags; + REGINFO *infop; + REGMAINT *rp; +{ + int ret; + + if ((ret = __db_mutex_init(dbenv, mutexp, offset, flags)) != 0) + return (ret); + /* + * !!! + * Since __db_mutex_init is a macro, we may not be + * using the 'offset' as it is only used for one type + * of mutex. We COMPQUIET it here, after the call above. + */ + COMPQUIET(offset, 0); + + if (!F_ISSET(mutexp, MUTEX_THREAD)) + ret = __db_shreg_locks_record(dbenv, mutexp, infop, rp); + /* + * If we couldn't record it and we are returning an error, + * we need to destroy the mutex we just created. + */ + if (ret) + __db_mutex_destroy(mutexp); + + return (ret); +} + +/* + * __db_shreg_maintinit -- + * Initialize a region's maintenance information. + * + * PUBLIC: void __db_shreg_maintinit __P((REGINFO *, void *addr, size_t)); + */ +void +__db_shreg_maintinit(infop, addr, size) + REGINFO *infop; + void *addr; + size_t size; +{ + REGMAINT *rp; + + rp = (REGMAINT *)addr; + memset(addr, 0, sizeof(REGMAINT)); + rp->reglocks = size / sizeof(roff_t); + rp->regmutex_hint = R_OFFSET(infop, &rp->regmutexes[0]); +} +#endif /* MUTEX_SYSTEM_RESOURCES */ diff --git a/bdb/mutex/uts4_cc.s b/bdb/mutex/uts4_cc.s new file mode 100644 index 00000000000..ee5f4143bde --- /dev/null +++ b/bdb/mutex/uts4_cc.s @@ -0,0 +1,21 @@ + / + / int uts_lock ( int *p, int i ); + / Update the lock word pointed to by p with the + / value i, using compare-and-swap. + / Returns 0 if update was successful. + / Returns 1 if update failed. + / + entry uts_lock + uts_lock: + using .,r15 + st r2,8(sp) / Save R2 + l r2,64+0(sp) / R2 -> word to update + slr r0, r0 / R0 = current lock value must be 0 + l r1,64+4(sp) / R1 = new lock value + cs r0,r1,0(r2) / Try the update ... + be x / ... Success. Return 0 + la r0,1 / ... Failure. Return 1 + x: / + l r2,8(sp) / Restore R2 + b 2(,r14) / Return to caller + drop r15 |