summaryrefslogtreecommitdiff
path: root/bdb/mutex
diff options
context:
space:
mode:
authorunknown <tim@threads.polyesthetic.msg>2001-03-04 19:42:05 -0500
committerunknown <tim@threads.polyesthetic.msg>2001-03-04 19:42:05 -0500
commit07dc15a5b0fafaf0a0bcde2768b34aad2f3825fa (patch)
tree9dd732e08dba156ee3d7635caedc0dc3107ecac6 /bdb/mutex
parent542e1c18dc5bf80665df55ffa04a48d986945259 (diff)
downloadmariadb-git-07dc15a5b0fafaf0a0bcde2768b34aad2f3825fa.tar.gz
Import changeset
Diffstat (limited to 'bdb/mutex')
-rw-r--r--bdb/mutex/README108
-rw-r--r--bdb/mutex/mut_fcntl.c174
-rw-r--r--bdb/mutex/mut_pthread.c328
-rw-r--r--bdb/mutex/mut_tas.c200
-rw-r--r--bdb/mutex/mutex.c253
-rw-r--r--bdb/mutex/uts4_cc.s21
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