1 /*-------------------------------------------------------------------------
4 * Hardware-dependent implementation of spinlocks.
7 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * $PostgreSQL: pgsql/src/backend/storage/lmgr/s_lock.c,v 1.23 2003/12/27 20:58:58 tgl Exp $
14 *-------------------------------------------------------------------------
21 #include "storage/s_lock.h"
25 * s_lock_stuck() - complain about a stuck spinlock
28 s_lock_stuck(volatile slock_t *lock, const char *file, int line)
30 #if defined(S_LOCK_TEST)
32 "\nStuck spinlock (%p) detected at %s:%d.\n",
36 elog(PANIC, "stuck spinlock (%p) detected at %s:%d",
43 * s_lock(lock) - platform-independent portion of waiting for a spinlock.
46 s_lock(volatile slock_t *lock, const char *file, int line)
49 * We loop tightly for awhile, then delay using select() and try
50 * again. Preferably, "awhile" should be a small multiple of the
51 * maximum time we expect a spinlock to be held. 100 iterations seems
52 * about right. In most multi-CPU scenarios, the spinlock is probably
53 * held by a process on another CPU and will be released before we
54 * finish 100 iterations. However, on a uniprocessor, the tight loop
55 * is just a waste of cycles, so don't iterate thousands of times.
57 * Once we do decide to block, we use randomly increasing select()
58 * delays. The first delay is 10 msec, then the delay randomly
59 * increases to about one second, after which we reset to 10 msec and
60 * start again. The idea here is that in the presence of heavy
61 * contention we need to increase the delay, else the spinlock holder
62 * may never get to run and release the lock. (Consider situation
63 * where spinlock holder has been nice'd down in priority by the
64 * scheduler --- it will not get scheduled until all would-be
65 * acquirers are sleeping, so if we always use a 10-msec sleep, there
66 * is a real possibility of starvation.) But we can't just clamp the
67 * delay to an upper bound, else it would take a long time to make a
68 * reasonable number of tries.
70 * We time out and declare error after NUM_DELAYS delays (thus, exactly
71 * that many tries). With the given settings, this will usually take
72 * 3 or so minutes. It seems better to fix the total number of tries
73 * (and thus the probability of unintended failure) than to fix the
76 * The select() delays are measured in centiseconds (0.01 sec) because 10
77 * msec is a common resolution limit at the OS level.
79 #define SPINS_PER_DELAY 100
80 #define NUM_DELAYS 1000
81 #define MIN_DELAY_CSEC 1
82 #define MAX_DELAY_CSEC 100
86 int cur_delay = MIN_DELAY_CSEC;
91 /* CPU-specific delay each time through the loop */
94 /* Block the process every SPINS_PER_DELAY tries */
95 if (++spins > SPINS_PER_DELAY)
97 if (++delays > NUM_DELAYS)
98 s_lock_stuck(lock, file, line);
100 delay.tv_sec = cur_delay / 100;
101 delay.tv_usec = (cur_delay % 100) * 10000;
102 (void) select(0, NULL, NULL, NULL, &delay);
104 #if defined(S_LOCK_TEST)
105 fprintf(stdout, "*");
109 /* increase delay by a random fraction between 1X and 2X */
110 cur_delay += (int) (cur_delay *
111 (((double) random()) / ((double) MAX_RANDOM_VALUE)) + 0.5);
112 /* wrap back to minimum delay when max is exceeded */
113 if (cur_delay > MAX_DELAY_CSEC)
114 cur_delay = MIN_DELAY_CSEC;
122 * Various TAS implementations that cannot live in s_lock.h as no inline
123 * definition exists (yet).
124 * In the future, get rid of tas.[cso] and fold it into this file.
126 * If you change something here, you will likely need to modify s_lock.h too,
127 * because the definitions for these are split between this file and s_lock.h.
131 #ifdef HAVE_SPINLOCKS /* skip spinlocks if requested */
134 #if defined(__GNUC__)
137 * All the gcc flavors that are not inlined
141 #if defined(__m68k__)
143 tas_dummy() /* really means: extern int tas(slock_t
146 __asm__ __volatile__(
150 movel sp@(0x4),a0 \n\
160 #endif /* __m68k__ */
163 #if defined(__mips__) && !defined(__sgi)
167 __asm__ __volatile__(
171 .frame $sp, 0, $31 \n\
181 .livereg 0x2000FF0E,0x00000FFF \n\
188 #endif /* __mips__ && !__sgi */
191 #else /* not __GNUC__ */
200 tas_dummy() /* really means: extern int tas(slock_t
209 asm("|#PROLOGUE# 1");
210 asm(" movel sp@(0x4),a0");
213 asm(" moveq #-128,d0");
223 #if defined(__sparc__) || defined(__sparc)
225 * sparc machines not using gcc
228 tas_dummy() /* really means: extern int tas(slock_t
231 asm(".seg \"data\"");
232 asm(".seg \"text\"");
236 * Sparc atomic test and set (sparc calls it "atomic load-store")
238 asm("ldstub [%r8], %r8");
242 #endif /* __sparc || __sparc__ */
245 #endif /* not __GNUC__ */
247 #endif /* HAVE_SPINLOCKS */
251 /*****************************************************************************/
252 #if defined(S_LOCK_TEST)
255 * test program for verifying a port's spinlock support.
258 volatile slock_t test_lock;
263 srandom((unsigned int) time(NULL));
265 S_INIT_LOCK(&test_lock);
267 if (!S_LOCK_FREE(&test_lock))
269 printf("S_LOCK_TEST: failed, lock not initialized\n");
275 if (S_LOCK_FREE(&test_lock))
277 printf("S_LOCK_TEST: failed, lock not locked\n");
281 S_UNLOCK(&test_lock);
283 if (!S_LOCK_FREE(&test_lock))
285 printf("S_LOCK_TEST: failed, lock not unlocked\n");
291 if (S_LOCK_FREE(&test_lock))
293 printf("S_LOCK_TEST: failed, lock not re-locked\n");
297 printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS);
298 printf(" exit with a 'stuck spinlock' message\n");
299 printf(" if S_LOCK() and TAS() are working.\n");
302 s_lock(&test_lock, __FILE__, __LINE__);
304 printf("S_LOCK_TEST: failed, lock not locked\n");
308 #endif /* S_LOCK_TEST */