1*0Sstevel@tonic-gate /*- 2*0Sstevel@tonic-gate * See the file LICENSE for redistribution information. 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * Copyright (c) 1996, 1997, 1998 5*0Sstevel@tonic-gate * Sleepycat Software. All rights reserved. 6*0Sstevel@tonic-gate */ 7*0Sstevel@tonic-gate 8*0Sstevel@tonic-gate #include "config.h" 9*0Sstevel@tonic-gate 10*0Sstevel@tonic-gate #ifndef lint 11*0Sstevel@tonic-gate static const char sccsid[] = "@(#)mutex.c 10.52 (Sleepycat) 11/8/98"; 12*0Sstevel@tonic-gate #endif /* not lint */ 13*0Sstevel@tonic-gate 14*0Sstevel@tonic-gate #ifndef NO_SYSTEM_INCLUDES 15*0Sstevel@tonic-gate #include <sys/types.h> 16*0Sstevel@tonic-gate 17*0Sstevel@tonic-gate #include <errno.h> 18*0Sstevel@tonic-gate #include <fcntl.h> 19*0Sstevel@tonic-gate #include <stdlib.h> 20*0Sstevel@tonic-gate #include <string.h> 21*0Sstevel@tonic-gate #include <unistd.h> 22*0Sstevel@tonic-gate #endif 23*0Sstevel@tonic-gate 24*0Sstevel@tonic-gate #include "db_int.h" 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate #ifdef HAVE_SPINLOCKS 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate #ifdef HAVE_FUNC_AIX 29*0Sstevel@tonic-gate #define TSL_INIT(x) 30*0Sstevel@tonic-gate #define TSL_SET(x) (!_check_lock(x, 0, 1)) 31*0Sstevel@tonic-gate #define TSL_UNSET(x) _clear_lock(x, 0) 32*0Sstevel@tonic-gate #endif 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate #ifdef HAVE_ASSEM_MC68020_GCC 35*0Sstevel@tonic-gate #include "68020.gcc" 36*0Sstevel@tonic-gate #endif 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate #if defined(HAVE_FUNC_MSEM) 39*0Sstevel@tonic-gate /* 40*0Sstevel@tonic-gate * !!! 41*0Sstevel@tonic-gate * Do not remove the MSEM_IF_NOWAIT flag. The problem is that if a single 42*0Sstevel@tonic-gate * process makes two msem_lock() calls in a row, the second one returns an 43*0Sstevel@tonic-gate * error. We depend on the fact that we can lock against ourselves in the 44*0Sstevel@tonic-gate * locking subsystem, where we set up a mutex so that we can block ourselves. 45*0Sstevel@tonic-gate * Tested on OSF1 v4.0. 46*0Sstevel@tonic-gate */ 47*0Sstevel@tonic-gate #define TSL_INIT(x) (msem_init(x, MSEM_UNLOCKED) == NULL) 48*0Sstevel@tonic-gate #define TSL_INIT_ERROR 1 49*0Sstevel@tonic-gate #define TSL_SET(x) (!msem_lock(x, MSEM_IF_NOWAIT)) 50*0Sstevel@tonic-gate #define TSL_UNSET(x) msem_unlock(x, 0) 51*0Sstevel@tonic-gate #endif 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate #ifdef HAVE_FUNC_RELIANT 54*0Sstevel@tonic-gate #define TSL_INIT(x) initspin(x, 1) 55*0Sstevel@tonic-gate #define TSL_SET(x) (cspinlock(x) == 0) 56*0Sstevel@tonic-gate #define TSL_UNSET(x) spinunlock(x) 57*0Sstevel@tonic-gate #endif 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate #ifdef HAVE_FUNC_SGI 60*0Sstevel@tonic-gate #define TSL_INIT(x) (init_lock(x) != 0) 61*0Sstevel@tonic-gate #define TSL_INIT_ERROR 1 62*0Sstevel@tonic-gate #define TSL_SET(x) (!acquire_lock(x)) 63*0Sstevel@tonic-gate #define TSL_UNSET(x) release_lock(x) 64*0Sstevel@tonic-gate #endif 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate #ifdef HAVE_FUNC_SOLARIS 67*0Sstevel@tonic-gate /* 68*0Sstevel@tonic-gate * Semaphore calls don't work on Solaris 5.5. 69*0Sstevel@tonic-gate * 70*0Sstevel@tonic-gate * #define TSL_INIT(x) (sema_init(x, 1, USYNC_PROCESS, NULL) != 0) 71*0Sstevel@tonic-gate * #define TSL_INIT_ERROR 1 72*0Sstevel@tonic-gate * #define TSL_SET(x) (sema_wait(x) == 0) 73*0Sstevel@tonic-gate * #define TSL_UNSET(x) sema_post(x) 74*0Sstevel@tonic-gate */ 75*0Sstevel@tonic-gate #define TSL_INIT(x) 76*0Sstevel@tonic-gate #define TSL_SET(x) (_lock_try(x)) 77*0Sstevel@tonic-gate #define TSL_UNSET(x) _lock_clear(x) 78*0Sstevel@tonic-gate #endif 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate #ifdef HAVE_FUNC_VMS 81*0Sstevel@tonic-gate #include <builtins.h> 82*0Sstevel@tonic-gate #ifdef __ALPHA 83*0Sstevel@tonic-gate #define TSL_SET(tsl) (!__TESTBITSSI(tsl, 0)) 84*0Sstevel@tonic-gate #else /* __VAX */ 85*0Sstevel@tonic-gate #define TSL_SET(tsl) (!(int)_BBSSI(0, tsl)) 86*0Sstevel@tonic-gate #endif 87*0Sstevel@tonic-gate #define TSL_UNSET(tsl) (*(tsl) = 0) 88*0Sstevel@tonic-gate #define TSL_INIT(tsl) TSL_UNSET(tsl) 89*0Sstevel@tonic-gate #endif 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate #ifdef HAVE_ASSEM_PARISC_GCC 92*0Sstevel@tonic-gate #include "parisc.gcc" 93*0Sstevel@tonic-gate #endif 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate #ifdef HAVE_ASSEM_SCO_CC 96*0Sstevel@tonic-gate #include "sco.cc" 97*0Sstevel@tonic-gate #endif 98*0Sstevel@tonic-gate 99*0Sstevel@tonic-gate #ifdef HAVE_ASSEM_SPARC_GCC 100*0Sstevel@tonic-gate #include "sparc.gcc" 101*0Sstevel@tonic-gate #endif 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate #ifdef HAVE_ASSEM_UTS4_CC 104*0Sstevel@tonic-gate #define TSL_INIT(x) 105*0Sstevel@tonic-gate #define TSL_SET(x) (!uts_lock(x, 1)) 106*0Sstevel@tonic-gate #define TSL_UNSET(x) (*(x) = 0) 107*0Sstevel@tonic-gate #endif 108*0Sstevel@tonic-gate 109*0Sstevel@tonic-gate #ifdef HAVE_ASSEM_X86_GCC 110*0Sstevel@tonic-gate #include "x86.gcc" 111*0Sstevel@tonic-gate #endif 112*0Sstevel@tonic-gate 113*0Sstevel@tonic-gate #ifdef WIN16 114*0Sstevel@tonic-gate /* Win16 spinlocks are simple because we cannot possibly be preempted. */ 115*0Sstevel@tonic-gate #define TSL_INIT(tsl) 116*0Sstevel@tonic-gate #define TSL_SET(tsl) (*(tsl) = 1) 117*0Sstevel@tonic-gate #define TSL_UNSET(tsl) (*(tsl) = 0) 118*0Sstevel@tonic-gate #endif 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate #if defined(_WIN32) 121*0Sstevel@tonic-gate /* 122*0Sstevel@tonic-gate * XXX 123*0Sstevel@tonic-gate * DBDB this needs to be byte-aligned!! 124*0Sstevel@tonic-gate */ 125*0Sstevel@tonic-gate #define TSL_INIT(tsl) 126*0Sstevel@tonic-gate #define TSL_SET(tsl) (!InterlockedExchange((PLONG)tsl, 1)) 127*0Sstevel@tonic-gate #define TSL_UNSET(tsl) (*(tsl) = 0) 128*0Sstevel@tonic-gate #endif 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate #endif /* HAVE_SPINLOCKS */ 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate /* 133*0Sstevel@tonic-gate * __db_mutex_init -- 134*0Sstevel@tonic-gate * Initialize a DB mutex structure. 135*0Sstevel@tonic-gate * 136*0Sstevel@tonic-gate * PUBLIC: int __db_mutex_init __P((db_mutex_t *, u_int32_t)); 137*0Sstevel@tonic-gate */ 138*0Sstevel@tonic-gate int 139*0Sstevel@tonic-gate __db_mutex_init(mp, off) 140*0Sstevel@tonic-gate db_mutex_t *mp; 141*0Sstevel@tonic-gate u_int32_t off; 142*0Sstevel@tonic-gate { 143*0Sstevel@tonic-gate #ifdef DIAGNOSTIC 144*0Sstevel@tonic-gate if ((ALIGNTYPE)mp & (MUTEX_ALIGNMENT - 1)) { 145*0Sstevel@tonic-gate (void)fprintf(stderr, 146*0Sstevel@tonic-gate "MUTEX ERROR: mutex NOT %d-byte aligned!\n", 147*0Sstevel@tonic-gate MUTEX_ALIGNMENT); 148*0Sstevel@tonic-gate abort(); 149*0Sstevel@tonic-gate } 150*0Sstevel@tonic-gate #endif 151*0Sstevel@tonic-gate memset(mp, 0, sizeof(db_mutex_t)); 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate #ifdef HAVE_SPINLOCKS 154*0Sstevel@tonic-gate COMPQUIET(off, 0); 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate #ifdef TSL_INIT_ERROR 157*0Sstevel@tonic-gate if (TSL_INIT(&mp->tsl_resource)) 158*0Sstevel@tonic-gate return (errno); 159*0Sstevel@tonic-gate #else 160*0Sstevel@tonic-gate TSL_INIT(&mp->tsl_resource); 161*0Sstevel@tonic-gate #endif 162*0Sstevel@tonic-gate mp->spins = __os_spin(); 163*0Sstevel@tonic-gate #else 164*0Sstevel@tonic-gate mp->off = off; 165*0Sstevel@tonic-gate #endif 166*0Sstevel@tonic-gate return (0); 167*0Sstevel@tonic-gate } 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate #define MS(n) ((n) * 1000) /* Milliseconds to micro-seconds. */ 170*0Sstevel@tonic-gate #define SECOND (MS(1000)) /* A second's worth of micro-seconds. */ 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate /* 173*0Sstevel@tonic-gate * __db_mutex_lock 174*0Sstevel@tonic-gate * Lock on a mutex, logically blocking if necessary. 175*0Sstevel@tonic-gate * 176*0Sstevel@tonic-gate * PUBLIC: int __db_mutex_lock __P((db_mutex_t *, int)); 177*0Sstevel@tonic-gate */ 178*0Sstevel@tonic-gate int 179*0Sstevel@tonic-gate __db_mutex_lock(mp, fd) 180*0Sstevel@tonic-gate db_mutex_t *mp; 181*0Sstevel@tonic-gate int fd; 182*0Sstevel@tonic-gate { 183*0Sstevel@tonic-gate u_long usecs; 184*0Sstevel@tonic-gate #ifdef HAVE_SPINLOCKS 185*0Sstevel@tonic-gate int nspins; 186*0Sstevel@tonic-gate #else 187*0Sstevel@tonic-gate struct flock k_lock; 188*0Sstevel@tonic-gate pid_t mypid; 189*0Sstevel@tonic-gate int locked; 190*0Sstevel@tonic-gate #endif 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate if (!DB_GLOBAL(db_mutexlocks)) 193*0Sstevel@tonic-gate return (0); 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate #ifdef HAVE_SPINLOCKS 196*0Sstevel@tonic-gate COMPQUIET(fd, 0); 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate for (usecs = MS(1);;) { 199*0Sstevel@tonic-gate /* Try and acquire the uncontested resource lock for N spins. */ 200*0Sstevel@tonic-gate for (nspins = mp->spins; nspins > 0; --nspins) 201*0Sstevel@tonic-gate if (TSL_SET(&mp->tsl_resource)) { 202*0Sstevel@tonic-gate #ifdef DIAGNOSTIC 203*0Sstevel@tonic-gate if (mp->pid != 0) { 204*0Sstevel@tonic-gate (void)fprintf(stderr, 205*0Sstevel@tonic-gate "MUTEX ERROR: __db_mutex_lock: lock currently locked\n"); 206*0Sstevel@tonic-gate abort(); 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate mp->pid = getpid(); 209*0Sstevel@tonic-gate #endif 210*0Sstevel@tonic-gate if (usecs == MS(1)) 211*0Sstevel@tonic-gate ++mp->mutex_set_nowait; 212*0Sstevel@tonic-gate else 213*0Sstevel@tonic-gate ++mp->mutex_set_wait; 214*0Sstevel@tonic-gate return (0); 215*0Sstevel@tonic-gate } 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate /* Yield the processor; wait 1ms initially, up to 1 second. */ 218*0Sstevel@tonic-gate __os_yield(usecs); 219*0Sstevel@tonic-gate if ((usecs <<= 1) > SECOND) 220*0Sstevel@tonic-gate usecs = SECOND; 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate /* NOTREACHED */ 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate #else /* !HAVE_SPINLOCKS */ 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate /* Initialize the lock. */ 227*0Sstevel@tonic-gate k_lock.l_whence = SEEK_SET; 228*0Sstevel@tonic-gate k_lock.l_start = mp->off; 229*0Sstevel@tonic-gate k_lock.l_len = 1; 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate for (locked = 0, mypid = getpid();;) { 232*0Sstevel@tonic-gate /* 233*0Sstevel@tonic-gate * Wait for the lock to become available; wait 1ms initially, 234*0Sstevel@tonic-gate * up to 1 second. 235*0Sstevel@tonic-gate */ 236*0Sstevel@tonic-gate for (usecs = MS(1); mp->pid != 0;) { 237*0Sstevel@tonic-gate __os_yield(usecs); 238*0Sstevel@tonic-gate if ((usecs <<= 1) > SECOND) 239*0Sstevel@tonic-gate usecs = SECOND; 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate /* Acquire an exclusive kernel lock. */ 243*0Sstevel@tonic-gate k_lock.l_type = F_WRLCK; 244*0Sstevel@tonic-gate if (fcntl(fd, F_SETLKW, &k_lock)) 245*0Sstevel@tonic-gate return (errno); 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate /* If the resource tsl is still available, it's ours. */ 248*0Sstevel@tonic-gate if (mp->pid == 0) { 249*0Sstevel@tonic-gate locked = 1; 250*0Sstevel@tonic-gate mp->pid = mypid; 251*0Sstevel@tonic-gate } 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate /* Release the kernel lock. */ 254*0Sstevel@tonic-gate k_lock.l_type = F_UNLCK; 255*0Sstevel@tonic-gate if (fcntl(fd, F_SETLK, &k_lock)) 256*0Sstevel@tonic-gate return (errno); 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate /* 259*0Sstevel@tonic-gate * If we got the resource tsl we're done. 260*0Sstevel@tonic-gate * 261*0Sstevel@tonic-gate * !!! 262*0Sstevel@tonic-gate * We can't check to see if the lock is ours, because we may 263*0Sstevel@tonic-gate * be trying to block ourselves in the lock manager, and so 264*0Sstevel@tonic-gate * the holder of the lock that's preventing us from getting 265*0Sstevel@tonic-gate * the lock may be us! (Seriously.) 266*0Sstevel@tonic-gate */ 267*0Sstevel@tonic-gate if (locked) 268*0Sstevel@tonic-gate break; 269*0Sstevel@tonic-gate } 270*0Sstevel@tonic-gate return (0); 271*0Sstevel@tonic-gate #endif /* !HAVE_SPINLOCKS */ 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate /* 275*0Sstevel@tonic-gate * __db_mutex_unlock -- 276*0Sstevel@tonic-gate * Release a lock. 277*0Sstevel@tonic-gate * 278*0Sstevel@tonic-gate * PUBLIC: int __db_mutex_unlock __P((db_mutex_t *, int)); 279*0Sstevel@tonic-gate */ 280*0Sstevel@tonic-gate int 281*0Sstevel@tonic-gate __db_mutex_unlock(mp, fd) 282*0Sstevel@tonic-gate db_mutex_t *mp; 283*0Sstevel@tonic-gate int fd; 284*0Sstevel@tonic-gate { 285*0Sstevel@tonic-gate if (!DB_GLOBAL(db_mutexlocks)) 286*0Sstevel@tonic-gate return (0); 287*0Sstevel@tonic-gate 288*0Sstevel@tonic-gate #ifdef DIAGNOSTIC 289*0Sstevel@tonic-gate if (mp->pid == 0) { 290*0Sstevel@tonic-gate (void)fprintf(stderr, 291*0Sstevel@tonic-gate "MUTEX ERROR: __db_mutex_unlock: lock already unlocked\n"); 292*0Sstevel@tonic-gate abort(); 293*0Sstevel@tonic-gate } 294*0Sstevel@tonic-gate #endif 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate #ifdef HAVE_SPINLOCKS 297*0Sstevel@tonic-gate COMPQUIET(fd, 0); 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate #ifdef DIAGNOSTIC 300*0Sstevel@tonic-gate mp->pid = 0; 301*0Sstevel@tonic-gate #endif 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate /* Release the resource tsl. */ 304*0Sstevel@tonic-gate TSL_UNSET(&mp->tsl_resource); 305*0Sstevel@tonic-gate #else 306*0Sstevel@tonic-gate /* 307*0Sstevel@tonic-gate * Release the resource tsl. We don't have to acquire any locks 308*0Sstevel@tonic-gate * because processes trying to acquire the lock are checking for 309*0Sstevel@tonic-gate * a pid of 0, not a specific value. 310*0Sstevel@tonic-gate */ 311*0Sstevel@tonic-gate mp->pid = 0; 312*0Sstevel@tonic-gate #endif 313*0Sstevel@tonic-gate return (0); 314*0Sstevel@tonic-gate } 315