Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / mutex / mutex.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)mutex.c       10.28 (Sleepycat) 10/31/97";
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 <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #endif
24
25 #include "db_int.h"
26 #include "common_ext.h"
27
28 #ifdef HAVE_SPINLOCKS
29
30 #ifdef HAVE_FUNC_AIX
31 #define TSL_INIT(x)
32 #define TSL_SET(x)      (!_check_lock(x, 0, 1))
33 #define TSL_UNSET(x)    _clear_lock(x, 0)
34 #endif
35
36 #ifdef HAVE_ASSEM_MC68020_GCC
37 #include "68020.gcc"
38 #endif
39
40 #if defined(HAVE_FUNC_MSEM)
41 /*
42  * XXX
43  * Should we not use MSEM_IF_NOWAIT and let the system block for us?
44  * I've no idea if this will block all threads in the process or not.
45  */
46 #define TSL_INIT(x)     msem_init(x, MSEM_UNLOCKED)
47 #define TSL_SET(x)      (!msem_lock(x, MSEM_IF_NOWAIT))
48 #define TSL_UNSET(x)    msem_unlock(x, 0)
49 #endif
50
51 #ifdef HAVE_FUNC_SGI
52 #define TSL_INIT(x)     init_lock(x)
53 #define TSL_SET(x)      (!acquire_lock(x))
54 #define TSL_UNSET(x)    release_lock(x)
55 #endif
56
57 #ifdef HAVE_FUNC_SOLARIS
58 /*
59  * Semaphore calls don't work on Solaris 5.5.
60  *
61  * #define      TSL_INIT(x)     sema_init(x, 1, USYNC_PROCESS, NULL)
62  * #define      TSL_SET(x)      (sema_wait(x) == 0)
63  * #define      TSL_UNSET(x)    sema_post(x)
64  */
65 #define TSL_INIT(x)
66 #define TSL_SET(x)      (_lock_try(x))
67 #define TSL_UNSET(x)    _lock_clear(x)
68 #endif
69
70 #ifdef HAVE_ASSEM_SCO_CC
71 #include "sco.cc"
72 #endif
73
74 #ifdef HAVE_ASSEM_SPARC_GCC
75 #include "sparc.gcc"
76 #endif
77
78 #ifdef HAVE_ASSEM_UTS4_CC
79 #define TSL_INIT(x)
80 #define TSL_SET(x)      (!uts_lock(x, 1))
81 #define TSL_UNSET(x)    (*(x) = 0)
82 #endif
83
84 #ifdef HAVE_ASSEM_X86_GCC
85 #include "x86.gcc"
86 #endif
87
88 #if defined(_WIN32)
89 /* DBDB this needs to be byte-aligned!! */
90 #define TSL_INIT(tsl)
91 #define TSL_SET(tsl)    (!InterlockedExchange((PLONG)tsl, 1))
92 #define TSL_UNSET(tsl)  (*(tsl) = 0)
93 #endif
94
95 #ifdef macintosh
96 /* Mac spinlocks are simple because we cannot possibly be preempted. */
97 #define TSL_INIT(tsl)
98 #define TSL_SET(tsl)    (*(tsl) = 1)
99 #define TSL_UNSET(tsl)  (*(tsl) = 0)
100 #endif
101
102 #endif /* HAVE_SPINLOCKS */
103
104 #ifdef  MORE_THAN_ONE_PROCESSOR
105 #define TSL_DEFAULT_SPINS       5       /* Default spins before block. */
106 #else
107 #define TSL_DEFAULT_SPINS       1       /* Default spins before block. */
108 #endif
109
110 /*
111  * __db_mutex_init --
112  *      Initialize a DB mutex structure.
113  *
114  * PUBLIC: void __db_mutex_init __P((db_mutex_t *, off_t));
115  */
116 void
117 __db_mutex_init(mp, off)
118         db_mutex_t *mp;
119         off_t off;
120 {
121 #ifdef DEBUG
122         if ((ALIGNTYPE)mp & (MUTEX_ALIGNMENT - 1)) {
123                 (void)fprintf(stderr,
124                     "MUTEX ERROR: mutex NOT %d-byte aligned!\n",
125                     MUTEX_ALIGNMENT);
126                 abort();
127         }
128 #endif
129         memset(mp, 0, sizeof(db_mutex_t));
130
131 #ifdef HAVE_SPINLOCKS
132         TSL_INIT(&mp->tsl_resource);
133 #else
134         mp->off = off;
135 #endif
136 }
137
138 #define MS(n)           ((n) * 1000)    /* Milliseconds to micro-seconds. */
139 #define SECOND          (MS(1000))      /* A second's worth of micro-seconds. */
140
141 /*
142  * __db_mutex_lock
143  *      Lock on a mutex, logically blocking if necessary.
144  *
145  * PUBLIC: int __db_mutex_lock __P((db_mutex_t *, int));
146  */
147 int
148 __db_mutex_lock(mp, fd)
149         db_mutex_t *mp;
150         int fd;
151 {
152         u_long usecs;
153
154 #ifdef HAVE_SPINLOCKS
155         int nspins;
156
157         for (usecs = MS(10);;) {
158                 /*
159                  * Try and acquire the uncontested resource lock for
160                  * TSL_DEFAULT_SPINS.
161                  */
162                 for (nspins = TSL_DEFAULT_SPINS; nspins > 0; --nspins)
163                         if (TSL_SET(&mp->tsl_resource)) {
164 #ifdef DEBUG
165                                 if (mp->pid != 0) {
166                                         (void)fprintf(stderr,
167                     "MUTEX ERROR: __db_mutex_lock: lock currently locked\n");
168                                         abort();
169                                 }
170                                 mp->pid = getpid();
171 #endif
172                                 if (usecs == MS(10))
173                                         ++mp->mutex_set_nowait;
174                                 else
175                                         ++mp->mutex_set_wait;
176                                 return (0);
177                         }
178
179                 /* Yield the processor; wait 10ms initially, up to 1 second. */
180                 if (__db_yield == NULL || __db_yield() != 0) {
181                         (void)__db_sleep(0, usecs);
182                         if ((usecs <<= 1) > SECOND)
183                                 usecs = SECOND;
184                 }
185         }
186         /* NOTREACHED */
187
188 #else /* !HAVE_SPINLOCKS */
189         struct flock k_lock;
190         pid_t mypid;
191         int locked;
192
193         /* Initialize the lock. */
194         k_lock.l_whence = SEEK_SET;
195         k_lock.l_start = mp->off;
196         k_lock.l_len = 1;
197
198         for (locked = 0, mypid = getpid();;) {
199                 /*
200                  * Wait for the lock to become available; wait 10ms initially,
201                  * up to 1 second.
202                  */
203                 for (usecs = MS(10); mp->pid != 0;)
204                         if (__db_yield == NULL || __db_yield() != 0) {
205                                 (void)__db_sleep(0, usecs);
206                                 if ((usecs <<= 1) > SECOND)
207                                         usecs = SECOND;
208                         }
209
210                 /* Acquire an exclusive kernel lock. */
211                 k_lock.l_type = F_WRLCK;
212                 if (fcntl(fd, F_SETLKW, &k_lock))
213                         return (1);
214
215                 /* If the resource tsl is still available, it's ours. */
216                 if (mp->pid == 0) {
217                         locked = 1;
218                         mp->pid = mypid;
219                 }
220
221                 /* Release the kernel lock. */
222                 k_lock.l_type = F_UNLCK;
223                 if (fcntl(fd, F_SETLK, &k_lock))
224                         return (1);
225
226                 /*
227                  * If we got the resource tsl we're done.
228                  *
229                  * !!!
230                  * We can't check to see if the lock is ours, because we may
231                  * be trying to block ourselves in the lock manager, and so
232                  * the holder of the lock that's preventing us from getting
233                  * the lock may be us!  (Seriously.)
234                  */
235                 if (locked)
236                         break;
237         }
238         return (0);
239 #endif /* !HAVE_SPINLOCKS */
240 }
241
242 /*
243  * __db_mutex_unlock --
244  *      Release a lock.
245  *
246  * PUBLIC: int __db_mutex_unlock __P((db_mutex_t *, int));
247  */
248 int
249 __db_mutex_unlock(mp, fd)
250         db_mutex_t *mp;
251         int fd;
252 {
253 #ifdef DEBUG
254         if (mp->pid == 0) {
255                 (void)fprintf(stderr,
256             "MUTEX ERROR: __db_mutex_unlock: lock already unlocked\n");
257                 abort();
258         }
259 #endif
260
261 #ifdef HAVE_SPINLOCKS
262 #ifdef DEBUG
263         mp->pid = 0;
264 #endif
265
266         /* Release the resource tsl. */
267         TSL_UNSET(&mp->tsl_resource);
268 #else
269         /*
270          * Release the resource tsl.  We don't have to acquire any locks
271          * because processes trying to acquire the lock are checking for
272          * a pid of 0, not a specific value.
273          */
274         mp->pid = 0;
275 #endif
276         return (0);
277 }