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
__db_mutex_init(mp,off)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
__db_mutex_lock(mp,fd)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
__db_mutex_unlock(mp,fd)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