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