diff options
Diffstat (limited to 'bdb/dbinc/mutex.h')
-rw-r--r-- | bdb/dbinc/mutex.h | 879 |
1 files changed, 879 insertions, 0 deletions
diff --git a/bdb/dbinc/mutex.h b/bdb/dbinc/mutex.h new file mode 100644 index 00000000000..41bb1b4bb59 --- /dev/null +++ b/bdb/dbinc/mutex.h @@ -0,0 +1,879 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996-2002 + * Sleepycat Software. All rights reserved. + * + * $Id: mutex.h,v 11.71 2002/09/10 01:36:48 bostic Exp $ + */ + +#ifndef _DB_MUTEX_H_ +#define _DB_MUTEX_H_ + +/* + * Some of the Berkeley DB ports require single-threading at various + * places in the code. In those cases, these #defines will be set. + */ +#define DB_BEGIN_SINGLE_THREAD +#define DB_END_SINGLE_THREAD + +/********************************************************************* + * POSIX.1 pthreads interface. + *********************************************************************/ +#ifdef HAVE_MUTEX_PTHREADS +#include <pthread.h> + +#define MUTEX_FIELDS \ + pthread_mutex_t mutex; /* Mutex. */ \ + pthread_cond_t cond; /* Condition variable. */ +#endif + +/********************************************************************* + * Solaris lwp threads interface. + * + * !!! + * We use LWP mutexes on Solaris instead of UI or POSIX mutexes (both of + * which are available), for two reasons. First, the Solaris C library + * includes versions of the both UI and POSIX thread mutex interfaces, but + * they are broken in that they don't support inter-process locking, and + * there's no way to detect it, e.g., calls to configure the mutexes for + * inter-process locking succeed without error. So, we use LWP mutexes so + * that we don't fail in fairly undetectable ways because the application + * wasn't linked with the appropriate threads library. Second, there were + * bugs in SunOS 5.7 (Solaris 7) where if an application loaded the C library + * before loading the libthread/libpthread threads libraries (e.g., by using + * dlopen to load the DB library), the pwrite64 interface would be translated + * into a call to pwrite and DB would drop core. + *********************************************************************/ +#ifdef HAVE_MUTEX_SOLARIS_LWP +/* + * XXX + * Don't change <synch.h> to <sys/lwp.h> -- although lwp.h is listed in the + * Solaris manual page as the correct include to use, it causes the Solaris + * compiler on SunOS 2.6 to fail. + */ +#include <synch.h> + +#define MUTEX_FIELDS \ + lwp_mutex_t mutex; /* Mutex. */ \ + lwp_cond_t cond; /* Condition variable. */ +#endif + +/********************************************************************* + * Solaris/Unixware threads interface. + *********************************************************************/ +#ifdef HAVE_MUTEX_UI_THREADS +#include <thread.h> +#include <synch.h> + +#define MUTEX_FIELDS \ + mutex_t mutex; /* Mutex. */ \ + cond_t cond; /* Condition variable. */ +#endif + +/********************************************************************* + * AIX C library functions. + *********************************************************************/ +#ifdef HAVE_MUTEX_AIX_CHECK_LOCK +#include <sys/atomic_op.h> +typedef int tsl_t; +#define MUTEX_ALIGN sizeof(int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) 0 +#define MUTEX_SET(x) (!_check_lock(x, 0, 1)) +#define MUTEX_UNSET(x) _clear_lock(x, 0) +#endif +#endif + +/********************************************************************* + * General C library functions (msemaphore). + * + * !!! + * Check for HPPA as a special case, because it requires unusual alignment, + * and doesn't support semaphores in malloc(3) or shmget(2) memory. + * + * !!! + * Do not remove the MSEM_IF_NOWAIT flag. The problem is that if a single + * process makes two msem_lock() calls in a row, the second one returns an + * error. We depend on the fact that we can lock against ourselves in the + * locking subsystem, where we set up a mutex so that we can block ourselves. + * Tested on OSF1 v4.0. + *********************************************************************/ +#ifdef HAVE_MUTEX_HPPA_MSEM_INIT +#define MUTEX_NO_MALLOC_LOCKS +#define MUTEX_NO_SHMGET_LOCKS + +#define MUTEX_ALIGN 16 +#endif + +#if defined(HAVE_MUTEX_MSEM_INIT) || defined(HAVE_MUTEX_HPPA_MSEM_INIT) +#include <sys/mman.h> +typedef msemaphore tsl_t; + +#ifndef MUTEX_ALIGN +#define MUTEX_ALIGN sizeof(int) +#endif + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) (msem_init(x, MSEM_UNLOCKED) <= (msemaphore *)0) +#define MUTEX_SET(x) (!msem_lock(x, MSEM_IF_NOWAIT)) +#define MUTEX_UNSET(x) msem_unlock(x, 0) +#endif +#endif + +/********************************************************************* + * Plan 9 library functions. + *********************************************************************/ +#ifdef HAVE_MUTEX_PLAN9 +typedef Lock tsl_t; + +#define MUTEX_ALIGN sizeof(int) + +#define MUTEX_INIT(x) (memset(x, 0, sizeof(Lock)), 0) +#define MUTEX_SET(x) canlock(x) +#define MUTEX_UNSET(x) unlock(x) +#endif + +/********************************************************************* + * Reliant UNIX C library functions. + *********************************************************************/ +#ifdef HAVE_MUTEX_RELIANTUNIX_INITSPIN +#include <ulocks.h> +typedef spinlock_t tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) (initspin(x, 1), 0) +#define MUTEX_SET(x) (cspinlock(x) == 0) +#define MUTEX_UNSET(x) spinunlock(x) +#endif +#endif + +/********************************************************************* + * General C library functions (POSIX 1003.1 sema_XXX). + * + * !!! + * Never selected by autoconfig in this release (semaphore calls are known + * to not work in Solaris 5.5). + *********************************************************************/ +#ifdef HAVE_MUTEX_SEMA_INIT +#include <synch.h> +typedef sema_t tsl_t; +#define MUTEX_ALIGN sizeof(int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_DESTROY(x) sema_destroy(x) +#define MUTEX_INIT(x) (sema_init(x, 1, USYNC_PROCESS, NULL) != 0) +#define MUTEX_SET(x) (sema_wait(x) == 0) +#define MUTEX_UNSET(x) sema_post(x) +#endif +#endif + +/********************************************************************* + * SGI C library functions. + *********************************************************************/ +#ifdef HAVE_MUTEX_SGI_INIT_LOCK +#include <abi_mutex.h> +typedef abilock_t tsl_t; +#define MUTEX_ALIGN sizeof(int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) (init_lock(x) != 0) +#define MUTEX_SET(x) (!acquire_lock(x)) +#define MUTEX_UNSET(x) release_lock(x) +#endif +#endif + +/********************************************************************* + * Solaris C library functions. + * + * !!! + * These are undocumented functions, but they're the only ones that work + * correctly as far as we know. + *********************************************************************/ +#ifdef HAVE_MUTEX_SOLARIS_LOCK_TRY +#include <sys/machlock.h> +typedef lock_t tsl_t; +#define MUTEX_ALIGN sizeof(int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) 0 +#define MUTEX_SET(x) _lock_try(x) +#define MUTEX_UNSET(x) _lock_clear(x) +#endif +#endif + +/********************************************************************* + * VMS. + *********************************************************************/ +#ifdef HAVE_MUTEX_VMS +#include <sys/mman.h>; +#include <builtins.h> +typedef unsigned char tsl_t; +#define MUTEX_ALIGN sizeof(unsigned int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#ifdef __ALPHA +#define MUTEX_SET(tsl) (!__TESTBITSSI(tsl, 0)) +#else /* __VAX */ +#define MUTEX_SET(tsl) (!(int)_BBSSI(0, tsl)) +#endif +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * VxWorks + * Use basic binary semaphores in VxWorks, as we currently do not need + * any special features. We do need the ability to single-thread the + * entire system, however, because VxWorks doesn't support the open(2) + * flag O_EXCL, the mechanism we normally use to single thread access + * when we're first looking for a DB environment. + *********************************************************************/ +#ifdef HAVE_MUTEX_VXWORKS +#include "taskLib.h" +typedef SEM_ID tsl_t; +#define MUTEX_ALIGN sizeof(unsigned int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_SET(tsl) (semTake((*tsl), WAIT_FOREVER) == OK) +#define MUTEX_UNSET(tsl) (semGive((*tsl))) +#define MUTEX_INIT(tsl) \ + ((*(tsl) = semBCreate(SEM_Q_FIFO, SEM_FULL)) == NULL) +#define MUTEX_DESTROY(tsl) semDelete(*tsl) +#endif + +/* + * Use the taskLock() mutex to eliminate a race where two tasks are + * trying to initialize the global lock at the same time. + */ +#undef DB_BEGIN_SINGLE_THREAD +#define DB_BEGIN_SINGLE_THREAD \ +do { \ + if (DB_GLOBAL(db_global_init)) \ + (void)semTake(DB_GLOBAL(db_global_lock), WAIT_FOREVER); \ + else { \ + taskLock(); \ + if (DB_GLOBAL(db_global_init)) { \ + taskUnlock(); \ + (void)semTake(DB_GLOBAL(db_global_lock), \ + WAIT_FOREVER); \ + continue; \ + } \ + DB_GLOBAL(db_global_lock) = \ + semBCreate(SEM_Q_FIFO, SEM_EMPTY); \ + if (DB_GLOBAL(db_global_lock) != NULL) \ + DB_GLOBAL(db_global_init) = 1; \ + taskUnlock(); \ + } \ +} while (DB_GLOBAL(db_global_init) == 0) +#undef DB_END_SINGLE_THREAD +#define DB_END_SINGLE_THREAD (void)semGive(DB_GLOBAL(db_global_lock)) +#endif + +/********************************************************************* + * Win16 + * + * Win16 spinlocks are simple because we cannot possibly be preempted. + * + * !!! + * We should simplify this by always returning a no-need-to-lock lock + * when we initialize the mutex. + *********************************************************************/ +#ifdef HAVE_MUTEX_WIN16 +typedef unsigned int tsl_t; +#define MUTEX_ALIGN sizeof(unsigned int) + +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) 0 +#define MUTEX_SET(tsl) (*(tsl) = 1) +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#endif +#endif + +/********************************************************************* + * Win32 + *********************************************************************/ +#ifdef HAVE_MUTEX_WIN32 +#define MUTEX_FIELDS \ + LONG tas; \ + LONG nwaiters; \ + union { \ + HANDLE event; /* Windows event HANDLE for wakeups */ \ + u_int32_t id; /* ID used for shared mutexes */ \ + } /* anonymous */; + +#if defined(LOAD_ACTUAL_MUTEX_CODE) +#define MUTEX_SET(tsl) (!InterlockedExchange((PLONG)tsl, 1)) +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * 68K/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_68K_GCC_ASSEMBLY +typedef unsigned char tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * For gcc/68K, 0 is clear, 1 is set. + */ +#define MUTEX_SET(tsl) ({ \ + register tsl_t *__l = (tsl); \ + int __r; \ + asm volatile("tas %1; \n \ + seq %0" \ + : "=dm" (__r), "=m" (*__l) \ + : "1" (*__l) \ + ); \ + __r & 1; \ +}) + +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * ALPHA/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_ALPHA_GCC_ASSEMBLY +typedef u_int32_t tsl_t; +#define MUTEX_ALIGN 4 + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * For gcc/alpha. Should return 0 if could not acquire the lock, 1 if + * lock was acquired properly. + */ +#ifdef __GNUC__ +static inline int +MUTEX_SET(tsl_t *tsl) { + register tsl_t *__l = tsl; + register tsl_t __r; + asm volatile( + "1: ldl_l %0,%2\n" + " blbs %0,2f\n" + " or $31,1,%0\n" + " stl_c %0,%1\n" + " beq %0,3f\n" + " mb\n" + " br 3f\n" + "2: xor %0,%0\n" + "3:" + : "=&r"(__r), "=m"(*__l) : "1"(*__l) : "memory"); + return __r; +} + +/* + * Unset mutex. Judging by Alpha Architecture Handbook, the mb instruction + * might be necessary before unlocking + */ +static inline int +MUTEX_UNSET(tsl_t *tsl) { + asm volatile(" mb\n"); + return *tsl = 0; +} +#endif + +#ifdef __DECC +#include <alpha/builtins.h> +#define MUTEX_SET(tsl) (__LOCK_LONG_RETRY((tsl), 1) != 0) +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#endif + +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * ARM/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_ARM_GCC_ASSEMBLY +typedef unsigned char tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * For arm/gcc, 0 is clear, 1 is set. + */ +#define MUTEX_SET(tsl) ({ \ + int __r; \ + asm volatile("swpb %0, %1, [%2]" \ + : "=r" (__r) \ + : "0" (1), "r" (tsl) \ + : "memory" \ + ); \ + __r & 1; \ +}) + +#define MUTEX_UNSET(tsl) (*(volatile tsl_t *)(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * HPPA/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_HPPA_GCC_ASSEMBLY +typedef u_int32_t tsl_t; +#define MUTEX_ALIGN 16 + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * The PA-RISC has a "load and clear" instead of a "test and set" instruction. + * The 32-bit word used by that instruction must be 16-byte aligned. We could + * use the "aligned" attribute in GCC but that doesn't work for stack variables. + */ +#define MUTEX_SET(tsl) ({ \ + register tsl_t *__l = (tsl); \ + int __r; \ + asm volatile("ldcws 0(%1),%0" : "=r" (__r) : "r" (__l)); \ + __r & 1; \ +}) + +#define MUTEX_UNSET(tsl) (*(tsl) = -1) +#define MUTEX_INIT(tsl) (MUTEX_UNSET(tsl), 0) +#endif +#endif + +/********************************************************************* + * IA64/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_IA64_GCC_ASSEMBLY +typedef unsigned char tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * For gcc/ia64, 0 is clear, 1 is set. + */ +#define MUTEX_SET(tsl) ({ \ + register tsl_t *__l = (tsl); \ + long __r; \ + asm volatile("xchg1 %0=%1,%3" : "=r"(__r), "=m"(*__l) : "1"(*__l), "r"(1));\ + __r ^ 1; \ +}) + +/* + * Store through a "volatile" pointer so we get a store with "release" + * semantics. + */ +#define MUTEX_UNSET(tsl) (*(volatile unsigned char *)(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * PowerPC/gcc assembly. + *********************************************************************/ +#if defined(HAVE_MUTEX_PPC_GENERIC_GCC_ASSEMBLY) || \ + (HAVE_MUTEX_PPC_APPLE_GCC_ASSEMBLY) +typedef u_int32_t tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * The PowerPC does a sort of pseudo-atomic locking. You set up a + * 'reservation' on a chunk of memory containing a mutex by loading the + * mutex value with LWARX. If the mutex has an 'unlocked' (arbitrary) + * value, you then try storing into it with STWCX. If no other process or + * thread broke your 'reservation' by modifying the memory containing the + * mutex, then the STCWX succeeds; otherwise it fails and you try to get + * a reservation again. + * + * While mutexes are explicitly 4 bytes, a 'reservation' applies to an + * entire cache line, normally 32 bytes, aligned naturally. If the mutex + * lives near data that gets changed a lot, there's a chance that you'll + * see more broken reservations than you might otherwise. The only + * situation in which this might be a problem is if one processor is + * beating on a variable in the same cache block as the mutex while another + * processor tries to acquire the mutex. That's bad news regardless + * because of the way it bashes caches, but if you can't guarantee that a + * mutex will reside in a relatively quiescent cache line, you might + * consider padding the mutex to force it to live in a cache line by + * itself. No, you aren't guaranteed that cache lines are 32 bytes. Some + * embedded processors use 16-byte cache lines, while some 64-bit + * processors use 128-bit cache lines. But assuming a 32-byte cache line + * won't get you into trouble for now. + * + * If mutex locking is a bottleneck, then you can speed it up by adding a + * regular LWZ load before the LWARX load, so that you can test for the + * common case of a locked mutex without wasting cycles making a reservation. + * + * 'set' mutexes have the value 1, like on Intel; the returned value from + * MUTEX_SET() is 1 if the mutex previously had its low bit clear, 0 otherwise. + * + * Mutexes on Mac OS X work the same way as the standard PowerPC version, but + * the assembler syntax is subtly different -- the standard PowerPC version + * assembles but doesn't work correctly. This version makes (unnecessary?) + * use of a stupid linker trick: __db_mutex_tas_dummy is never called, but the + * ___db_mutex_set label is used as a function name. + */ +#ifdef HAVE_MUTEX_PPC_APPLE_GCC_ASSEMBLY +extern int __db_mutex_set __P((volatile tsl_t *)); +void +__db_mutex_tas_dummy() +{ + __asm__ __volatile__(" \n\ + .globl ___db_mutex_set \n\ +___db_mutex_set: \n\ + lwarx r5,0,r3 \n\ + cmpwi r5,0 \n\ + bne fail \n\ + addi r5,r5,1 \n\ + stwcx. r5,0,r3 \n\ + beq success \n\ +fail: \n\ + li r3,0 \n\ + blr \n\ +success: \n\ + li r3,1 \n\ + blr"); +} +#define MUTEX_SET(tsl) __db_mutex_set(tsl) +#endif +#ifdef HAVE_MUTEX_PPC_GENERIC_GCC_ASSEMBLY +#define MUTEX_SET(tsl) ({ \ + int __one = 1; \ + int __r; \ + tsl_t *__l = (tsl); \ + asm volatile (" \ +0: \ + lwarx %0,0,%1; \ + cmpwi %0,0; \ + bne 1f; \ + stwcx. %2,0,%1; \ + bne- 0b; \ +1:" \ + : "=&r" (__r) \ + : "r" (__l), "r" (__one)); \ + !(__r & 1); \ +}) +#endif +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * S/390 32-bit assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_S390_GCC_ASSEMBLY +typedef int tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * For gcc/S390, 0 is clear, 1 is set. + */ +static inline int +MUTEX_SET(tsl_t *tsl) { \ + register tsl_t *__l = (tsl); \ + int __r; \ + asm volatile( \ + " la 1,%1\n" \ + " lhi 0,1\n" \ + " l %0,%1\n" \ + "0: cs %0,0,0(1)\n" \ + " jl 0b" \ + : "=&d" (__r), "+m" (*__l) \ + : : "0", "1", "cc"); \ + return !__r; \ +} + +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * SCO/cc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_SCO_X86_CC_ASSEMBLY +typedef unsigned char tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * UnixWare has threads in libthread, but OpenServer doesn't (yet). + * + * For cc/x86, 0 is clear, 1 is set. + */ + +#if defined(__USLC__) +asm int +_tsl_set(void *tsl) +{ +%mem tsl + movl tsl, %ecx + movl $1, %eax + lock + xchgb (%ecx),%al + xorl $1,%eax +} +#endif + +#define MUTEX_SET(tsl) _tsl_set(tsl) +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * Sparc/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_SPARC_GCC_ASSEMBLY +typedef unsigned char tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * + * The ldstub instruction takes the location specified by its first argument + * (a register containing a memory address) and loads its contents into its + * second argument (a register) and atomically sets the contents the location + * specified by its first argument to a byte of 1s. (The value in the second + * argument is never read, but only overwritten.) + * + * The stbar is needed for v8, and is implemented as membar #sync on v9, + * so is functional there as well. For v7, stbar may generate an illegal + * instruction and we have no way to tell what we're running on. Some + * operating systems notice and skip this instruction in the fault handler. + * + * For gcc/sparc, 0 is clear, 1 is set. + */ +#define MUTEX_SET(tsl) ({ \ + register tsl_t *__l = (tsl); \ + register tsl_t __r; \ + __asm__ volatile \ + ("ldstub [%1],%0; stbar" \ + : "=r"( __r) : "r" (__l)); \ + !__r; \ +}) + +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/********************************************************************* + * UTS/cc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_UTS_CC_ASSEMBLY +typedef int tsl_t; + +#define MUTEX_ALIGN sizeof(int) +#ifdef LOAD_ACTUAL_MUTEX_CODE +#define MUTEX_INIT(x) 0 +#define MUTEX_SET(x) (!uts_lock(x, 1)) +#define MUTEX_UNSET(x) (*(x) = 0) +#endif +#endif + +/********************************************************************* + * x86/gcc assembly. + *********************************************************************/ +#ifdef HAVE_MUTEX_X86_GCC_ASSEMBLY +typedef unsigned char tsl_t; + +#ifdef LOAD_ACTUAL_MUTEX_CODE +/* + * For gcc/x86, 0 is clear, 1 is set. + */ +#define MUTEX_SET(tsl) ({ \ + register tsl_t *__l = (tsl); \ + int __r; \ + asm volatile("movl $1,%%eax; lock; xchgb %1,%%al; xorl $1,%%eax"\ + : "=&a" (__r), "=m" (*__l) \ + : "1" (*__l) \ + ); \ + __r & 1; \ +}) + +#define MUTEX_UNSET(tsl) (*(tsl) = 0) +#define MUTEX_INIT(tsl) MUTEX_UNSET(tsl) +#endif +#endif + +/* + * Mutex alignment defaults to one byte. + * + * !!! + * Various systems require different alignments for mutexes (the worst we've + * seen so far is 16-bytes on some HP architectures). Malloc(3) is assumed + * to return reasonable alignment, all other mutex users must ensure proper + * alignment locally. + */ +#ifndef MUTEX_ALIGN +#define MUTEX_ALIGN 1 +#endif + +/* + * Mutex destruction defaults to a no-op. + */ +#ifdef LOAD_ACTUAL_MUTEX_CODE +#ifndef MUTEX_DESTROY +#define MUTEX_DESTROY(x) +#endif +#endif + +/* + * !!! + * These defines are separated into the u_int8_t flags stored in the + * mutex below, and the 32 bit flags passed to __db_mutex_setup. + * But they must co-exist and not overlap. Flags to __db_mutex_setup are: + * + * MUTEX_ALLOC - Use when the mutex to initialize needs to be allocated. + * The 'ptr' arg to __db_mutex_setup should be a DB_MUTEX ** whenever + * you use this flag. If this flag is not set, the 'ptr' arg is + * a DB_MUTEX *. + * MUTEX_NO_RECORD - Explicitly do not record the mutex in the region. + * Otherwise the mutex will be recorded by default. If you set + * this you need to understand why you don't need it recorded. The + * *only* ones not recorded are those that are part of region structures + * that only get destroyed when the regions are destroyed. + * MUTEX_NO_RLOCK - Explicitly do not lock the given region otherwise + * the region will be locked by default. + * MUTEX_SELF_BLOCK - Set if self blocking mutex. + * MUTEX_THREAD - Set if mutex is a thread-only mutex. + */ +#define MUTEX_IGNORE 0x001 /* Ignore, no lock required. */ +#define MUTEX_INITED 0x002 /* Mutex is successfully initialized */ +#define MUTEX_MPOOL 0x004 /* Allocated from mpool. */ +#define MUTEX_SELF_BLOCK 0x008 /* Must block self. */ +/* Flags only, may be larger than 0xff. */ +#define MUTEX_ALLOC 0x00000100 /* Allocate and init a mutex */ +#define MUTEX_NO_RECORD 0x00000200 /* Do not record lock */ +#define MUTEX_NO_RLOCK 0x00000400 /* Do not acquire region lock */ +#define MUTEX_THREAD 0x00000800 /* Thread-only mutex. */ + +/* Mutex. */ +struct __mutex_t { +#ifdef HAVE_MUTEX_THREADS +#ifdef MUTEX_FIELDS + MUTEX_FIELDS +#else + tsl_t tas; /* Test and set. */ +#endif + u_int32_t spins; /* Spins before block. */ + u_int32_t locked; /* !0 if locked. */ +#else + u_int32_t off; /* Byte offset to lock. */ + u_int32_t pid; /* Lock holder: 0 or process pid. */ +#endif + u_int32_t mutex_set_wait; /* Granted after wait. */ + u_int32_t mutex_set_nowait; /* Granted without waiting. */ + u_int32_t mutex_set_spin; /* Granted without spinning. */ + u_int32_t mutex_set_spins; /* Total number of spins. */ +#ifdef HAVE_MUTEX_SYSTEM_RESOURCES + roff_t reg_off; /* Shared lock info offset. */ +#endif + + u_int8_t flags; /* MUTEX_XXX */ +}; + +/* Redirect calls to the correct functions. */ +#ifdef HAVE_MUTEX_THREADS +#if defined(HAVE_MUTEX_PTHREADS) || \ + defined(HAVE_MUTEX_SOLARIS_LWP) || \ + defined(HAVE_MUTEX_UI_THREADS) +#define __db_mutex_init_int(a, b, c, d) __db_pthread_mutex_init(a, b, d) +#define __db_mutex_lock(a, b) __db_pthread_mutex_lock(a, b) +#define __db_mutex_unlock(a, b) __db_pthread_mutex_unlock(a, b) +#define __db_mutex_destroy(a) __db_pthread_mutex_destroy(a) +#elif defined(HAVE_MUTEX_WIN32) +#define __db_mutex_init_int(a, b, c, d) __db_win32_mutex_init(a, b, d) +#define __db_mutex_lock(a, b) __db_win32_mutex_lock(a, b) +#define __db_mutex_unlock(a, b) __db_win32_mutex_unlock(a, b) +#define __db_mutex_destroy(a) __db_win32_mutex_destroy(a) +#else +#define __db_mutex_init_int(a, b, c, d) __db_tas_mutex_init(a, b, d) +#define __db_mutex_lock(a, b) __db_tas_mutex_lock(a, b) +#define __db_mutex_unlock(a, b) __db_tas_mutex_unlock(a, b) +#define __db_mutex_destroy(a) __db_tas_mutex_destroy(a) +#endif +#else +#define __db_mutex_init_int(a, b, c, d) __db_fcntl_mutex_init(a, b, c) +#define __db_mutex_lock(a, b) __db_fcntl_mutex_lock(a, b) +#define __db_mutex_unlock(a, b) __db_fcntl_mutex_unlock(a, b) +#define __db_mutex_destroy(a) __db_fcntl_mutex_destroy(a) +#endif + +/* Redirect system resource calls to correct functions */ +#ifdef HAVE_MUTEX_SYSTEM_RESOURCES +#define __db_maintinit(a, b, c) __db_shreg_maintinit(a, b, c) +#define __db_shlocks_clear(a, b, c) __db_shreg_locks_clear(a, b, c) +#define __db_shlocks_destroy(a, b) __db_shreg_locks_destroy(a, b) +#define __db_mutex_init(a, b, c, d, e, f) \ + __db_shreg_mutex_init(a, b, c, d, e, f) +#else +#define __db_maintinit(a, b, c) +#define __db_shlocks_clear(a, b, c) +#define __db_shlocks_destroy(a, b) +#define __db_mutex_init(a, b, c, d, e, f) __db_mutex_init_int(a, b, c, d) +#endif + +/* + * Lock/unlock a mutex. If the mutex was marked as uninteresting, the thread + * of control can proceed without it. + * + * If the lock is for threads-only, then it was optionally not allocated and + * file handles aren't necessary, as threaded applications aren't supported by + * fcntl(2) locking. + */ +#ifdef DIAGNOSTIC + /* + * XXX + * We want to switch threads as often as possible. Yield every time + * we get a mutex to ensure contention. + */ +#define MUTEX_LOCK(dbenv, mp) \ + if (!F_ISSET((mp), MUTEX_IGNORE)) \ + DB_ASSERT(__db_mutex_lock(dbenv, mp) == 0); \ + if (F_ISSET(dbenv, DB_ENV_YIELDCPU)) \ + __os_yield(NULL, 1); +#else +#define MUTEX_LOCK(dbenv, mp) \ + if (!F_ISSET((mp), MUTEX_IGNORE)) \ + (void)__db_mutex_lock(dbenv, mp); +#endif +#define MUTEX_UNLOCK(dbenv, mp) \ + if (!F_ISSET((mp), MUTEX_IGNORE)) \ + (void)__db_mutex_unlock(dbenv, mp); +#define MUTEX_THREAD_LOCK(dbenv, mp) \ + if (mp != NULL) \ + MUTEX_LOCK(dbenv, mp) +#define MUTEX_THREAD_UNLOCK(dbenv, mp) \ + if (mp != NULL) \ + MUTEX_UNLOCK(dbenv, mp) + +/* + * We use a single file descriptor for fcntl(2) locking, and (generally) the + * object's offset in a shared region as the byte that we're locking. So, + * there's a (remote) possibility that two objects might have the same offsets + * such that the locks could conflict, resulting in deadlock. To avoid this + * possibility, we offset the region offset by a small integer value, using a + * different offset for each subsystem's locks. Since all region objects are + * suitably aligned, the offset guarantees that we don't collide with another + * region's objects. + */ +#define DB_FCNTL_OFF_GEN 0 /* Everything else. */ +#define DB_FCNTL_OFF_LOCK 1 /* Lock subsystem offset. */ +#define DB_FCNTL_OFF_MPOOL 2 /* Mpool subsystem offset. */ + +#ifdef HAVE_MUTEX_SYSTEM_RESOURCES +/* + * When the underlying mutexes require library (most likely heap) or system + * resources, we have to clean up when we discard mutexes (for the library + * resources) and both when discarding mutexes and after application failure + * (for the mutexes requiring system resources). This violates the rule that + * we never look at a shared region after application failure, but we've no + * other choice. In those cases, the #define HAVE_MUTEX_SYSTEM_RESOURCES is + * set. + * + * To support mutex release after application failure, allocate thread-handle + * mutexes in shared memory instead of in the heap. The number of slots we + * allocate for this purpose isn't configurable, but this tends to be an issue + * only on embedded systems where we don't expect large server applications. + */ +#define DB_MAX_HANDLES 100 /* Mutex slots for handles. */ +#endif +#endif /* !_DB_MUTEX_H_ */ |