]> granicus.if.org Git - postgresql/blob - src/include/storage/s_lock.h
Suppress compiler warnings in Vax and NS32K assembly code: 'register foo'
[postgresql] / src / include / storage / s_lock.h
1 /*-------------------------------------------------------------------------
2  *
3  * s_lock.h
4  *         This file contains the in-line portion of the implementation
5  *         of spinlocks.
6  *
7  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.92 2001/04/13 23:32:57 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16
17 /*----------
18  * DESCRIPTION
19  *      The public macros that must be provided are:
20  *
21  *      void S_INIT_LOCK(slock_t *lock)
22  *              Initialize a spinlock (to the unlocked state).
23  *
24  *      void S_LOCK(slock_t *lock)
25  *              Acquire a spinlock, waiting if necessary.
26  *              Time out and abort() if unable to acquire the lock in a
27  *              "reasonable" amount of time --- typically ~ 1 minute.
28  *
29  *      void S_UNLOCK(slock_t *lock)
30  *              Unlock a previously acquired lock.
31  *
32  *      bool S_LOCK_FREE(slock_t *lock)
33  *              Tests if the lock is free. Returns TRUE if free, FALSE if locked.
34  *              This does *not* change the state of the lock.
35  *
36  *      int TAS(slock_t *lock)
37  *              Atomic test-and-set instruction.  Attempt to acquire the lock,
38  *              but do *not* wait.      Returns 0 if successful, nonzero if unable
39  *              to acquire the lock.
40  *
41  *      TAS() is a lower-level part of the API, but is used directly in a
42  *      few places that want to do other things while waiting for a lock.
43  *      The S_LOCK() macro is equivalent to
44  *
45  *      void
46  *      S_LOCK(slock_t *lock)
47  *      {
48  *              unsigned        spins = 0;
49  *
50  *              while (TAS(lock))
51  *                      S_LOCK_SLEEP(lock, spins++, timeout);
52  *      }
53  *
54  *      where S_LOCK_SLEEP() checks for timeout and sleeps for a short
55  *      interval.  (The timeout is expressed in microseconds, or can be 0 for
56  *      "infinity".)  Callers that want to perform useful work while waiting
57  *      can write out this entire loop and insert the "useful work" inside
58  *      the loop.
59  *
60  *      CAUTION to TAS() callers: on some platforms TAS() may sometimes
61  *      report failure to acquire a lock even when the lock is not locked.
62  *      For example, on Alpha TAS() will "fail" if interrupted.  Therefore
63  *      TAS() must *always* be invoked in a retry loop as depicted, even when
64  *      you are certain the lock is free.
65  *
66  *      On most supported platforms, TAS() uses a tas() function written
67  *      in assembly language to execute a hardware atomic-test-and-set
68  *      instruction.  Equivalent OS-supplied mutex routines could be used too.
69  *
70  *      If no system-specific TAS() is available (ie, HAS_TEST_AND_SET is not
71  *      defined), then we fall back on an emulation that uses SysV semaphores.
72  *      This emulation will be MUCH MUCH MUCH slower than a proper TAS()
73  *      implementation, because of the cost of a kernel call per lock or unlock.
74  *      An old report is that Postgres spends around 40% of its time in semop(2)
75  *      when using the SysV semaphore code.
76  *
77  *      Note to implementors: there are default implementations for all these
78  *      macros at the bottom of the file.  Check if your platform can use
79  *      these or needs to override them.
80  *----------
81  */
82 #ifndef S_LOCK_H
83 #define S_LOCK_H
84
85 #include "storage/ipc.h"
86
87
88 #if defined(HAS_TEST_AND_SET)
89
90
91 #if defined(__GNUC__)
92 /*************************************************************************
93  * All the gcc inlines
94  */
95
96 /*
97  * Standard gcc asm format:
98  *
99         __asm__ __volatile__(
100                 "       command \n"
101                 "       command \n"
102                 "       command \n"
103 :               "=r"(_res)                      return value, in register
104 :               "r"(lock)                       argument, 'lock pointer', in register
105 :               "r0");                          inline code uses this register
106  */
107
108
109 #if defined(__i386__) && !defined(__QNX__)
110 #define TAS(lock) tas(lock)
111
112 static __inline__ int
113 tas(volatile slock_t *lock)
114 {
115         register slock_t _res = 1;
116
117         __asm__ __volatile__(
118                 "       lock                    \n"
119                 "       xchgb   %0,%1   \n"
120 :               "=q"(_res), "=m"(*lock)
121 :               "0"(_res));
122         return (int) _res;
123 }
124
125 #endif   /* __i386__ */
126
127
128 #ifdef __ia64__
129 #define TAS(lock) tas(lock)
130
131 static __inline__ int
132 tas(volatile slock_t *lock)
133 {
134         long int        ret;
135
136         __asm__ __volatile__(
137                 "       xchg4   %0=%1,%2        \n"
138 :               "=r"(ret), "=m"(*lock)
139 :               "r"(1), "1"(*lock)
140 :               "memory");
141
142         return (int) ret;
143 }
144
145 #endif   /* __ia64__ */
146
147
148 #if defined(__arm__) || defined(__arm__)
149 #define TAS(lock) tas(lock)
150
151 static __inline__ int
152 tas(volatile slock_t *lock)
153 {
154         register slock_t _res = 1;
155
156         __asm__ __volatile__(
157                 "       swpb    %0, %0, [%3]    \n"
158 :               "=r"(_res), "=m"(*lock)
159 :               "0"(_res), "r"(lock));
160         return (int) _res;
161 }
162
163 #endif   /* __arm__ */
164
165 #if defined(__s390__)
166 /*
167  * S/390 Linux
168  */
169 #define TAS(lock)          tas(lock)
170
171 static inline int
172 tas(volatile slock_t *lock)
173 {
174         int                     _res;
175
176         __asm__ __volatile__(
177                 "       la      1,1                     \n"
178                 "       l       2,%2            \n"
179                 "       slr 0,0                 \n"
180                 "       cs      0,1,0(2)        \n"
181                 "       lr      %1,0            \n"
182 :               "=m"(lock), "=d"(_res)
183 :               "m"(lock)
184 :               "0", "1", "2");
185
186         return (_res);
187 }
188
189 #endif   /* __s390__ */
190
191
192 #if defined(__sparc__)
193 #define TAS(lock) tas(lock)
194
195 static __inline__ int
196 tas(volatile slock_t *lock)
197 {
198         register slock_t _res = 1;
199
200         __asm__ __volatile__(
201                 "       ldstub  [%2], %0        \n"
202 :               "=r"(_res), "=m"(*lock)
203 :               "r"(lock));
204         return (int) _res;
205 }
206
207 #endif   /* __sparc__ */
208
209
210 #if defined(__mc68000__) && defined(__linux__)
211 #define TAS(lock) tas(lock)
212
213 static __inline__ int
214 tas(volatile slock_t *lock)
215 {
216         register int rv;
217
218         __asm__ __volatile__(
219                 "       clrl    %0              \n"
220                 "       tas             %1              \n"
221                 "       sne             %0              \n"
222 :               "=d"(rv), "=m"(*lock)
223 :               "1"(*lock)
224 :               "cc");
225
226         return rv;
227 }
228
229 #endif   /* defined(__mc68000__) && defined(__linux__) */
230
231
232 #if defined(NEED_VAX_TAS_ASM)
233 /*
234  * VAXen -- even multiprocessor ones
235  * (thanks to Tom Ivar Helbekkmo)
236  */
237 #define TAS(lock) tas(lock)
238
239 static __inline__ int
240 tas(volatile slock_t *lock)
241 {
242         register int    _res;
243
244         __asm__ __volatile__(
245                 "       movl    $1, r0                  \n"
246                 "       bbssi   $0, (%1), 1f    \n"
247                 "       clrl    r0                              \n"
248                 "1:     movl    r0, %0                  \n"
249 :               "=r"(_res)
250 :               "r"(lock)
251 :               "r0");
252         return _res;
253 }
254
255 #endif   /* NEED_VAX_TAS_ASM */
256
257
258 #if defined(NEED_NS32K_TAS_ASM)
259 #define TAS(lock) tas(lock)
260
261 static __inline__ int
262 tas(volatile slock_t *lock)
263 {
264         register int    _res;
265
266         __asm__ __volatile__(
267                 "       sbitb   0, %0   \n"
268                 "       sfsd    %1              \n"
269 :               "=m"(*lock), "=r"(_res));
270         return _res;
271 }
272
273 #endif   /* NEED_NS32K_TAS_ASM */
274
275
276
277 #else                                                   /* !__GNUC__ */
278
279 /***************************************************************************
280  * All non-gcc inlines
281  */
282
283 #if defined(NEED_I386_TAS_ASM) && defined(USE_UNIVEL_CC)
284 #define TAS(lock)       tas(lock)
285
286 asm int
287 tas(volatile slock_t *s_lock)
288 {
289 /* UNIVEL wants %mem in column 1, so we don't pg_indent this file */
290 %mem s_lock
291         pushl %ebx
292         movl s_lock, %ebx
293         movl $255, %eax
294         lock
295         xchgb %al, (%ebx)
296         popl %ebx
297 }
298
299 #endif   /* defined(NEED_I386_TAS_ASM) && defined(USE_UNIVEL_CC) */
300
301 #endif   /* defined(__GNUC__) */
302
303
304
305 /*************************************************************************
306  * These are the platforms that do not use inline assembler (and hence
307  * have common code for gcc and non-gcc compilers, if both are available).
308  */
309
310
311 #if defined(__alpha)
312
313 /*
314  * Correct multi-processor locking methods are explained in section 5.5.3
315  * of the Alpha AXP Architecture Handbook, which at this writing can be
316  * found at ftp://ftp.netbsd.org/pub/NetBSD/misc/dec-docs/index.html.
317  * For gcc we implement the handbook's code directly with inline assembler.
318  */
319 #if defined(__GNUC__)
320
321 #define TAS(lock)  tas(lock)
322 #define S_UNLOCK(lock)  \
323 do \
324 {\
325         __asm__ __volatile__ (" mb \n"); \
326         *(lock) = 0; \
327 } while (0)
328
329 static __inline__ int
330 tas(volatile slock_t *lock)
331 {
332         register slock_t _res;
333
334         __asm__ __volatile__(
335                 "       ldq             $0, %0  \n"
336                 "       bne             $0, 2f  \n"
337                 "       ldq_l   %1, %0  \n"
338                 "       bne             %1, 2f  \n"
339                 "       mov             1,  $0  \n"
340                 "       stq_c   $0, %0  \n"
341                 "       beq             $0, 2f  \n"
342                 "       mb                              \n"
343                 "       br              3f              \n"
344                 "2:     mov             1, %1   \n"
345                 "3:                                     \n"
346 :               "=m"(*lock), "=r"(_res)
347 :
348 :               "0");
349
350         return (int) _res;
351 }
352
353 #else                                                   /* !defined(__GNUC__) */
354
355 /*
356  * The Tru64 compiler doesn't support gcc-style inline asm, but it does
357  * have some builtin functions that accomplish much the same results.
358  * For simplicity, slock_t is defined as long (ie, quadword) on Alpha
359  * regardless of the compiler in use.  LOCK_LONG and UNLOCK_LONG only
360  * operate on an int (ie, longword), but that's OK as long as we define
361  * S_INIT_LOCK to zero out the whole quadword.
362  */
363
364 #include <alpha/builtins.h>
365
366 #define S_INIT_LOCK(lock)  (*(lock) = 0)
367 #define TAS(lock)                  (__LOCK_LONG_RETRY((lock), 1) == 0)
368 #define S_UNLOCK(lock)     __UNLOCK_LONG(lock)
369
370 #endif   /* defined(__GNUC__) */
371
372 #endif   /* __alpha */
373
374
375 #if defined(__hpux)
376 /*
377  * HP-UX (PA-RISC)
378  *
379  * Note that slock_t on PA-RISC is a structure instead of char
380  * (see include/port/hpux.h).
381  *
382  * a "set" slock_t has a single word cleared.  a "clear" slock_t has
383  * all words set to non-zero. tas() is in tas.s
384  */
385
386 #define S_UNLOCK(lock) \
387         do { \
388                 volatile slock_t *lock_ = (volatile slock_t *) (lock); \
389                 lock_->sema[0] = -1; \
390                 lock_->sema[1] = -1; \
391                 lock_->sema[2] = -1; \
392                 lock_->sema[3] = -1; \
393         } while (0)
394
395 #define S_LOCK_FREE(lock)       ( *(int *) (((long) (lock) + 15) & ~15) != 0)
396
397 #endif   /* __hpux */
398
399
400 #if defined(__QNX__)
401 /*
402  * QNX 4
403  *
404  * Note that slock_t under QNX is sem_t instead of char
405  */
406 #define TAS(lock)               (sem_trywait((lock)) < 0)
407 #define S_UNLOCK(lock)  sem_post((lock))
408 #define S_INIT_LOCK(lock)               sem_init((lock), 1, 1)
409 #define S_LOCK_FREE(lock)               ((lock)->value)
410 #endif   /* __QNX__ */
411
412
413 #if defined(__sgi)
414 /*
415  * SGI IRIX 5
416  * slock_t is defined as a unsigned long. We use the standard SGI
417  * mutex API.
418  *
419  * The following comment is left for historical reasons, but is probably
420  * not a good idea since the mutex ABI is supported.
421  *
422  * This stuff may be supplemented in the future with Masato Kataoka's MIPS-II
423  * assembly from his NECEWS SVR4 port, but we probably ought to retain this
424  * for the R3000 chips out there.
425  */
426 #include "mutex.h"
427 #define TAS(lock)       (test_and_set(lock,1))
428 #define S_UNLOCK(lock)  (test_then_and(lock,0))
429 #define S_INIT_LOCK(lock)       (test_then_and(lock,0))
430 #define S_LOCK_FREE(lock)       (test_then_add(lock,0) == 0)
431 #endif   /* __sgi */
432
433 #if defined(sinix)
434 /*
435  * SINIX / Reliant UNIX
436  * slock_t is defined as a struct abilock_t, which has a single unsigned long
437  * member. (Basically same as SGI)
438  *
439  */
440 #define TAS(lock)       (!acquire_lock(lock))
441 #define S_UNLOCK(lock)  release_lock(lock)
442 #define S_INIT_LOCK(lock)       init_lock(lock)
443 #define S_LOCK_FREE(lock)       (stat_lock(lock) == UNLOCKED)
444 #endif   /* sinix */
445
446
447 #if defined(_AIX)
448 /*
449  * AIX (POWER)
450  *
451  * Note that slock_t on POWER/POWER2/PowerPC is int instead of char
452  * (see storage/ipc.h).
453  */
454 #define TAS(lock)       cs((int *) (lock), 0, 1)
455 #endif   /* _AIX */
456
457
458 #if defined (nextstep)
459 /*
460  * NEXTSTEP (mach)
461  * slock_t is defined as a struct mutex.
462  */
463
464 #define S_LOCK(lock)    mutex_lock(lock)
465 #define S_UNLOCK(lock)  mutex_unlock(lock)
466 #define S_INIT_LOCK(lock)       mutex_init(lock)
467 /* For Mach, we have to delve inside the entrails of `struct mutex'.  Ick! */
468 #define S_LOCK_FREE(alock)      ((alock)->lock == 0)
469 #endif   /* nextstep */
470
471
472
473 #else                                                   /* !HAS_TEST_AND_SET */
474
475 /*
476  * Fake spinlock implementation using SysV semaphores --- slow and prone
477  * to fall foul of kernel limits on number of semaphores, so don't use this
478  * unless you must!
479  */
480
481 typedef struct
482 {
483         /* reference to semaphore used to implement this spinlock */
484         IpcSemaphoreId semId;
485         int                     sem;
486 } slock_t;
487
488 extern bool s_lock_free_sema(volatile slock_t *lock);
489 extern void s_unlock_sema(volatile slock_t *lock);
490 extern void s_init_lock_sema(volatile slock_t *lock);
491 extern int      tas_sema(volatile slock_t *lock);
492
493 #define S_LOCK_FREE(lock)       s_lock_free_sema(lock)
494 #define S_UNLOCK(lock)   s_unlock_sema(lock)
495 #define S_INIT_LOCK(lock)       s_init_lock_sema(lock)
496 #define TAS(lock)       tas_sema(lock)
497
498 #endif   /* HAS_TEST_AND_SET */
499
500
501
502 /****************************************************************************
503  * Default Definitions - override these above as needed.
504  */
505
506 #if !defined(S_LOCK)
507 #define S_LOCK(lock) \
508         do { \
509                 if (TAS(lock)) \
510                         s_lock((lock), __FILE__, __LINE__); \
511         } while (0)
512 #endif   /* S_LOCK */
513
514 #if !defined(S_LOCK_SLEEP)
515 #define S_LOCK_SLEEP(lock,spins,timeout) \
516         s_lock_sleep((spins), (timeout), 0, (lock), __FILE__, __LINE__)
517 #endif   /* S_LOCK_SLEEP */
518
519 #if !defined(S_LOCK_SLEEP_INTERVAL)
520 #define S_LOCK_SLEEP_INTERVAL(lock,spins,timeout,microsec) \
521         s_lock_sleep((spins), (timeout), (microsec), (lock), __FILE__, __LINE__)
522 #endif   /* S_LOCK_SLEEP_INTERVAL */
523
524 #if !defined(S_LOCK_FREE)
525 #define S_LOCK_FREE(lock)       (*(lock) == 0)
526 #endif   /* S_LOCK_FREE */
527
528 #if !defined(S_UNLOCK)
529 #define S_UNLOCK(lock)          (*(lock) = 0)
530 #endif   /* S_UNLOCK */
531
532 #if !defined(S_INIT_LOCK)
533 #define S_INIT_LOCK(lock)       S_UNLOCK(lock)
534 #endif   /* S_INIT_LOCK */
535
536 #if !defined(TAS)
537 extern int      tas(volatile slock_t *lock);            /* in port/.../tas.s, or
538                                                                                                  * s_lock.c */
539
540 #define TAS(lock)               tas(lock)
541 #endif   /* TAS */
542
543
544 /****************************************************************************
545  * Platform-independent out-of-line support routines
546  */
547
548 extern void s_lock(volatile slock_t *lock,
549            const char *file, const int line);
550 extern void s_lock_sleep(unsigned spins, int timeout, int microsec,
551                          volatile slock_t *lock,
552                          const char *file, const int line);
553
554 #endif   /* S_LOCK_H */