]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/s_lock.c
Replace out-of-line tas() assembly code for MIPS with a properly
[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-2005, 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.37 2005/08/25 17:17:09 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include <time.h>
19 #include <unistd.h>
20
21 #include "storage/s_lock.h"
22 #include "miscadmin.h"
23
24 /*
25  * s_lock_stuck() - complain about a stuck spinlock
26  */
27 static void
28 s_lock_stuck(volatile slock_t *lock, const char *file, int line)
29 {
30 #if defined(S_LOCK_TEST)
31         fprintf(stderr,
32                         "\nStuck spinlock (%p) detected at %s:%d.\n",
33                         lock, file, line);
34         exit(1);
35 #else
36         elog(PANIC, "stuck spinlock (%p) detected at %s:%d",
37                  lock, file, line);
38 #endif
39 }
40
41
42 /*
43  * s_lock(lock) - platform-independent portion of waiting for a spinlock.
44  */
45 void
46 s_lock(volatile slock_t *lock, const char *file, int line)
47 {
48         /*
49          * We loop tightly for awhile, then delay using pg_usleep() 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.
56          *
57          * Once we do decide to block, we use randomly increasing pg_usleep()
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.
69          *
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
74          * total time spent.
75          *
76          * The pg_usleep() delays are measured in centiseconds (0.01 sec) because
77          * 10 msec is a common resolution limit at the OS level.
78          */
79 #define SPINS_PER_DELAY         100
80 #define NUM_DELAYS                      1000
81 #define MIN_DELAY_CSEC          1
82 #define MAX_DELAY_CSEC          100
83
84         int                     spins = 0;
85         int                     delays = 0;
86         int                     cur_delay = MIN_DELAY_CSEC;
87
88         while (TAS(lock))
89         {
90                 /* CPU-specific delay each time through the loop */
91                 SPIN_DELAY();
92
93                 /* Block the process every SPINS_PER_DELAY tries */
94                 if (++spins > SPINS_PER_DELAY)
95                 {
96                         if (++delays > NUM_DELAYS)
97                                 s_lock_stuck(lock, file, line);
98
99                         pg_usleep(cur_delay * 10000L);
100
101 #if defined(S_LOCK_TEST)
102                         fprintf(stdout, "*");
103                         fflush(stdout);
104 #endif
105
106                         /* increase delay by a random fraction between 1X and 2X */
107                         cur_delay += (int) (cur_delay *
108                           (((double) random()) / ((double) MAX_RANDOM_VALUE)) + 0.5);
109                         /* wrap back to minimum delay when max is exceeded */
110                         if (cur_delay > MAX_DELAY_CSEC)
111                                 cur_delay = MIN_DELAY_CSEC;
112
113                         spins = 0;
114                 }
115         }
116 }
117
118 /*
119  * Various TAS implementations that cannot live in s_lock.h as no inline
120  * definition exists (yet).
121  * In the future, get rid of tas.[cso] and fold it into this file.
122  *
123  * If you change something here, you will likely need to modify s_lock.h too,
124  * because the definitions for these are split between this file and s_lock.h.
125  */
126
127
128 #ifdef HAVE_SPINLOCKS                   /* skip spinlocks if requested */
129
130
131 #if defined(__GNUC__)
132
133 /*
134  * All the gcc flavors that are not inlined
135  */
136
137
138 #if defined(__m68k__)
139 /* really means: extern int tas(slock_t* **lock); */
140 static void
141 tas_dummy()
142 {
143         __asm__         __volatile__(
144 #if defined(__NetBSD__) && defined(__ELF__)
145 /* no underscore for label and % for registers */
146                                                                                 "\
147 .global         tas                             \n\
148 tas:                                                    \n\
149                         movel   %sp@(0x4),%a0   \n\
150                         tas     %a0@            \n\
151                         beq     _success        \n\
152                         moveq   #-128,%d0       \n\
153                         rts                             \n\
154 _success:                                               \n\
155                         moveq   #0,%d0          \n\
156                         rts                             \n"
157 #else
158                                                                                 "\
159 .global         _tas                            \n\
160 _tas:                                                   \n\
161                         movel   sp@(0x4),a0     \n\
162                         tas     a0@                     \n\
163                         beq     _success        \n\
164                         moveq   #-128,d0        \n\
165                         rts                                     \n\
166 _success:                                               \n\
167                         moveq   #0,d0           \n\
168                         rts                                     \n"
169 #endif   /* __NetBSD__ && __ELF__ */
170 );
171 }
172 #endif   /* __m68k__ */
173
174
175 #else                                                   /* not __GNUC__ */
176
177 /*
178  * All non gcc
179  */
180
181
182 #if defined(sun3)
183 static void
184 tas_dummy()                                             /* really means: extern int tas(slock_t
185                                                                  * *lock); */
186 {
187         asm("LLA0:");
188         asm("   .data");
189         asm("   .text");
190         asm("|#PROC# 04");
191         asm("   .globl  _tas");
192         asm("_tas:");
193         asm("|#PROLOGUE# 1");
194         asm("   movel   sp@(0x4),a0");
195         asm("   tas a0@");
196         asm("   beq LLA1");
197         asm("   moveq   #-128,d0");
198         asm("   rts");
199         asm("LLA1:");
200         asm("   moveq   #0,d0");
201         asm("   rts");
202         asm("   .data");
203 }
204 #endif   /* sun3 */
205
206
207 #if defined(__sparc__) || defined(__sparc)
208 /*
209  * sparc machines not using gcc
210  */
211 static void
212 tas_dummy()                                             /* really means: extern int tas(slock_t
213                                                                  * *lock); */
214 {
215
216 #ifdef SUNOS_CC
217         asm(".seg \"data\"");
218         asm(".seg \"text\"");
219 #else
220         asm(".section \"data\"");
221         asm(".section \"text\"");
222 #endif
223
224         asm("_tas:");
225
226         /*
227          * Sparc atomic test and set (sparc calls it "atomic load-store")
228          */
229         asm("ldstub [%r8], %r8");
230         asm("retl");
231         asm("nop");
232 }
233 #endif   /* __sparc || __sparc__ */
234 #endif   /* not __GNUC__ */
235 #endif   /* HAVE_SPINLOCKS */
236
237
238
239 /*****************************************************************************/
240 #if defined(S_LOCK_TEST)
241
242 /*
243  * test program for verifying a port's spinlock support.
244  */
245
246 struct test_lock_struct
247 {
248         char            pad1;
249         slock_t         lock;
250         char            pad2;
251 };
252
253 volatile struct test_lock_struct test_lock;
254
255 int
256 main()
257 {
258         srandom((unsigned int) time(NULL));
259
260         test_lock.pad1 = test_lock.pad2 = 0x44;
261
262         S_INIT_LOCK(&test_lock.lock);
263
264         if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
265         {
266                 printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
267                 return 1;
268         }
269
270         if (!S_LOCK_FREE(&test_lock.lock))
271         {
272                 printf("S_LOCK_TEST: failed, lock not initialized\n");
273                 return 1;
274         }
275
276         S_LOCK(&test_lock.lock);
277
278         if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
279         {
280                 printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
281                 return 1;
282         }
283
284         if (S_LOCK_FREE(&test_lock.lock))
285         {
286                 printf("S_LOCK_TEST: failed, lock not locked\n");
287                 return 1;
288         }
289
290         S_UNLOCK(&test_lock.lock);
291
292         if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
293         {
294                 printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
295                 return 1;
296         }
297
298         if (!S_LOCK_FREE(&test_lock.lock))
299         {
300                 printf("S_LOCK_TEST: failed, lock not unlocked\n");
301                 return 1;
302         }
303
304         S_LOCK(&test_lock.lock);
305
306         if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
307         {
308                 printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
309                 return 1;
310         }
311
312         if (S_LOCK_FREE(&test_lock.lock))
313         {
314                 printf("S_LOCK_TEST: failed, lock not re-locked\n");
315                 return 1;
316         }
317
318         printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS);
319         printf("             exit with a 'stuck spinlock' message\n");
320         printf("             if S_LOCK() and TAS() are working.\n");
321         fflush(stdout);
322
323         s_lock(&test_lock.lock, __FILE__, __LINE__);
324
325         printf("S_LOCK_TEST: failed, lock not locked\n");
326         return 1;
327 }
328
329 #endif   /* S_LOCK_TEST */