xref: /illumos-gate/usr/src/cmd/sendmail/db/mutex/mutex.c (revision 7c478bd9)
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