]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/s_lock.c
When changing select() calls for delays into pg_usleep(), two comments
[postgresql] / src / backend / storage / lmgr / s_lock.c
1 /*-------------------------------------------------------------------------
2  *
3  * s_lock.c
4  *         Hardware-dependent implementation of spinlocks.
5  *
6  *
7  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/storage/lmgr/s_lock.c,v 1.27 2004/03/23 21:39:46 momjian Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include <unistd.h>
19
20 #include "storage/s_lock.h"
21 #include "miscadmin.h"
22
23 /*
24  * s_lock_stuck() - complain about a stuck spinlock
25  */
26 static void
27 s_lock_stuck(volatile slock_t *lock, const char *file, int line)
28 {
29 #if defined(S_LOCK_TEST)
30         fprintf(stderr,
31                         "\nStuck spinlock (%p) detected at %s:%d.\n",
32                         lock, file, line);
33         exit(1);
34 #else
35         elog(PANIC, "stuck spinlock (%p) detected at %s:%d",
36                  lock, file, line);
37 #endif
38 }
39
40
41 /*
42  * s_lock(lock) - platform-independent portion of waiting for a spinlock.
43  */
44 void
45 s_lock(volatile slock_t *lock, const char *file, int line)
46 {
47         /*
48          * We loop tightly for awhile, then delay using pg_usleep() and try
49          * again. Preferably, "awhile" should be a small multiple of the
50          * maximum time we expect a spinlock to be held.  100 iterations seems
51          * about right.  In most multi-CPU scenarios, the spinlock is probably
52          * held by a process on another CPU and will be released before we
53          * finish 100 iterations.  However, on a uniprocessor, the tight loop
54          * is just a waste of cycles, so don't iterate thousands of times.
55          *
56          * Once we do decide to block, we use randomly increasing pg_usleep()
57          * delays. The first delay is 10 msec, then the delay randomly
58          * increases to about one second, after which we reset to 10 msec and
59          * start again.  The idea here is that in the presence of heavy
60          * contention we need to increase the delay, else the spinlock holder
61          * may never get to run and release the lock.  (Consider situation
62          * where spinlock holder has been nice'd down in priority by the
63          * scheduler --- it will not get scheduled until all would-be
64          * acquirers are sleeping, so if we always use a 10-msec sleep, there
65          * is a real possibility of starvation.)  But we can't just clamp the
66          * delay to an upper bound, else it would take a long time to make a
67          * reasonable number of tries.
68          *
69          * We time out and declare error after NUM_DELAYS delays (thus, exactly
70          * that many tries).  With the given settings, this will usually take
71          * 3 or so minutes.  It seems better to fix the total number of tries
72          * (and thus the probability of unintended failure) than to fix the
73          * total time spent.
74          *
75          * The pg_usleep() delays are measured in centiseconds (0.01 sec) because 10
76          * msec is a common resolution limit at the OS level.
77          */
78 #define SPINS_PER_DELAY         100
79 #define NUM_DELAYS                      1000
80 #define MIN_DELAY_CSEC          1
81 #define MAX_DELAY_CSEC          100
82
83         int                     spins = 0;
84         int                     delays = 0;
85         int                     cur_delay = MIN_DELAY_CSEC;
86
87         while (TAS(lock))
88         {
89                 /* CPU-specific delay each time through the loop */
90                 SPIN_DELAY();
91
92                 /* Block the process every SPINS_PER_DELAY tries */
93                 if (++spins > SPINS_PER_DELAY)
94                 {
95                         if (++delays > NUM_DELAYS)
96                                 s_lock_stuck(lock, file, line);
97
98                         pg_usleep(cur_delay * 10000L);
99
100 #if defined(S_LOCK_TEST)
101                         fprintf(stdout, "*");
102                         fflush(stdout);
103 #endif
104
105                         /* increase delay by a random fraction between 1X and 2X */
106                         cur_delay += (int) (cur_delay *
107                           (((double) random()) / ((double) MAX_RANDOM_VALUE)) + 0.5);
108                         /* wrap back to minimum delay when max is exceeded */
109                         if (cur_delay > MAX_DELAY_CSEC)
110                                 cur_delay = MIN_DELAY_CSEC;
111
112                         spins = 0;
113                 }
114         }
115 }
116
117 /*
118  * Various TAS implementations that cannot live in s_lock.h as no inline
119  * definition exists (yet).
120  * In the future, get rid of tas.[cso] and fold it into this file.
121  *
122  * If you change something here, you will likely need to modify s_lock.h too,
123  * because the definitions for these are split between this file and s_lock.h.
124  */
125
126
127 #ifdef HAVE_SPINLOCKS   /* skip spinlocks if requested */
128
129
130 #if defined(__GNUC__)
131
132 /*
133  * All the gcc flavors that are not inlined
134  */
135
136
137 #if defined(__m68k__)
138 static void
139 tas_dummy()                                             /* really means: extern int tas(slock_t
140                                                                  * **lock); */
141 {
142         __asm__         __volatile__(
143                                                                                  "\
144 .global         _tas                            \n\
145 _tas:                                                   \n\
146                         movel   sp@(0x4),a0     \n\
147                         tas     a0@                     \n\
148                         beq     _success        \n\
149                         moveq   #-128,d0        \n\
150                         rts                                     \n\
151 _success:                                               \n\
152                         moveq   #0,d0           \n\
153                         rts                                     \n\
154 ");
155 }
156 #endif   /* __m68k__ */
157
158
159 #if defined(__mips__) && !defined(__sgi)
160 static void
161 tas_dummy()
162 {
163         __asm__         __volatile__(
164                                                                                  "\
165 .global tas                                             \n\
166 tas:                                                    \n\
167                         .frame  $sp, 0, $31     \n\
168                         .set push               \n\
169                         .set mips2              \n\
170                         ll              $14, 0($4)      \n\
171                         or              $15, $14, 1     \n\
172                         sc              $15, 0($4)      \n\
173                         .set pop                        \n\
174                         beq             $15, 0, fail\n\
175                         bne             $14, 0, fail\n\
176                         li              $2, 0           \n\
177                         .livereg 0x2000FF0E,0x00000FFF  \n\
178                         j               $31                     \n\
179 fail:                                                   \n\
180                         li              $2, 1           \n\
181                         j       $31                     \n\
182 ");
183 }
184 #endif   /* __mips__ && !__sgi */
185
186
187 #else                                                   /* not __GNUC__ */
188
189 /*
190  * All non gcc
191  */
192
193
194 #if defined(sun3)
195 static void
196 tas_dummy()                                             /* really means: extern int tas(slock_t
197                                                                  * *lock); */
198 {
199         asm("LLA0:");
200         asm("   .data");
201         asm("   .text");
202         asm("|#PROC# 04");
203         asm("   .globl  _tas");
204         asm("_tas:");
205         asm("|#PROLOGUE# 1");
206         asm("   movel   sp@(0x4),a0");
207         asm("   tas a0@");
208         asm("   beq LLA1");
209         asm("   moveq   #-128,d0");
210         asm("   rts");
211         asm("LLA1:");
212         asm("   moveq   #0,d0");
213         asm("   rts");
214         asm("   .data");
215 }
216 #endif   /* sun3 */
217
218
219 #if defined(__sparc__) || defined(__sparc)
220 /*
221  * sparc machines not using gcc
222  */
223 static void
224 tas_dummy()                                             /* really means: extern int tas(slock_t
225                                                                  * *lock); */
226 {
227         asm(".seg \"data\"");
228         asm(".seg \"text\"");
229         asm("_tas:");
230
231         /*
232          * Sparc atomic test and set (sparc calls it "atomic load-store")
233          */
234         asm("ldstub [%r8], %r8");
235         asm("retl");
236         asm("nop");
237 }
238 #endif   /* __sparc || __sparc__ */
239
240
241 #endif   /* not __GNUC__ */
242
243 #endif /* HAVE_SPINLOCKS */
244
245
246
247 /*****************************************************************************/
248 #if defined(S_LOCK_TEST)
249
250 /*
251  * test program for verifying a port's spinlock support.
252  */
253
254 volatile slock_t test_lock;
255
256 int
257 main()
258 {
259         srandom((unsigned int) time(NULL));
260
261         S_INIT_LOCK(&test_lock);
262
263         if (!S_LOCK_FREE(&test_lock))
264         {
265                 printf("S_LOCK_TEST: failed, lock not initialized\n");
266                 return 1;
267         }
268
269         S_LOCK(&test_lock);
270
271         if (S_LOCK_FREE(&test_lock))
272         {
273                 printf("S_LOCK_TEST: failed, lock not locked\n");
274                 return 1;
275         }
276
277         S_UNLOCK(&test_lock);
278
279         if (!S_LOCK_FREE(&test_lock))
280         {
281                 printf("S_LOCK_TEST: failed, lock not unlocked\n");
282                 return 1;
283         }
284
285         S_LOCK(&test_lock);
286
287         if (S_LOCK_FREE(&test_lock))
288         {
289                 printf("S_LOCK_TEST: failed, lock not re-locked\n");
290                 return 1;
291         }
292
293         printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS);
294         printf("             exit with a 'stuck spinlock' message\n");
295         printf("             if S_LOCK() and TAS() are working.\n");
296         fflush(stdout);
297
298         s_lock(&test_lock, __FILE__, __LINE__);
299
300         printf("S_LOCK_TEST: failed, lock not locked\n");
301         return 1;
302 }
303
304 #endif   /* S_LOCK_TEST */