]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/s_lock.c
This applied patch remove NEED_SPARC_TAS_ASM and instead uses __sparc ||
[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.18 2003/12/22 23:39:53 momjian Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include <sys/time.h>
19 #include <unistd.h>
20
21 #include "storage/s_lock.h"
22
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 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.
56          *
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.
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 select() delays are measured in centiseconds (0.01 sec) because 10
77          * 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         struct timeval delay;
88
89         while (TAS(lock))
90         {
91                 if (++spins > SPINS_PER_DELAY)
92                 {
93                         if (++delays > NUM_DELAYS)
94                                 s_lock_stuck(lock, file, line);
95
96                         delay.tv_sec = cur_delay / 100;
97                         delay.tv_usec = (cur_delay % 100) * 10000;
98                         (void) select(0, NULL, NULL, NULL, &delay);
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
123
124 #if defined(__GNUC__)
125 /*************************************************************************
126  * All the gcc flavors that are not inlined
127  */
128
129
130 #if defined(__m68k__)
131 static void
132 tas_dummy()                                             /* really means: extern int tas(slock_t
133                                                                  * **lock); */
134 {
135         __asm__         __volatile__(
136                                                                                  "\
137 .global         _tas                            \n\
138 _tas:                                                   \n\
139                         movel   sp@(0x4),a0     \n\
140                         tas     a0@                     \n\
141                         beq     _success        \n\
142                         moveq   #-128,d0        \n\
143                         rts                                     \n\
144 _success:                                               \n\
145                         moveq   #0,d0           \n\
146                         rts                                     \n\
147 ");
148 }
149 #endif   /* __m68k__ */
150
151 #if defined(__mips__) && !defined(__sgi)
152 static void
153 tas_dummy()
154 {
155         __asm__         __volatile__(
156                                                                                  "\
157 .global tas                                             \n\
158 tas:                                                    \n\
159                         .frame  $sp, 0, $31     \n\
160                         .set push               \n\
161                         .set mips2              \n\
162                         ll              $14, 0($4)      \n\
163                         or              $15, $14, 1     \n\
164                         sc              $15, 0($4)      \n\
165                         .set pop                        \n\
166                         beq             $15, 0, fail\n\
167                         bne             $14, 0, fail\n\
168                         li              $2, 0           \n\
169                         .livereg 0x2000FF0E,0x00000FFF  \n\
170                         j               $31                     \n\
171 fail:                                                   \n\
172                         li              $2, 1           \n\
173                         j       $31                     \n\
174 ");
175 }
176 #endif   /* __mips__ && !__sgi */
177
178 #else                                                   /* not __GNUC__ */
179 /***************************************************************************
180  * All non gcc
181  */
182
183
184
185 #if defined(sun3)
186 static void
187 tas_dummy()                                             /* really means: extern int tas(slock_t
188                                                                  * *lock); */
189 {
190         asm("LLA0:");
191         asm("   .data");
192         asm("   .text");
193         asm("|#PROC# 04");
194         asm("   .globl  _tas");
195         asm("_tas:");
196         asm("|#PROLOGUE# 1");
197         asm("   movel   sp@(0x4),a0");
198         asm("   tas a0@");
199         asm("   beq LLA1");
200         asm("   moveq   #-128,d0");
201         asm("   rts");
202         asm("LLA1:");
203         asm("   moveq   #0,d0");
204         asm("   rts");
205         asm("   .data");
206 }
207 #endif   /* sun3 */
208
209
210
211 #if defined(__sparc__) || defined(__sparc)
212 /*
213  * sparc machines not using gcc
214  */
215 static void
216 tas_dummy()                                             /* really means: extern int tas(slock_t
217                                                                  * *lock); */
218 {
219         asm(".seg \"data\"");
220         asm(".seg \"text\"");
221         asm("_tas:");
222
223         /*
224          * Sparc atomic test and set (sparc calls it "atomic load-store")
225          */
226         asm("ldstub [%r8], %r8");
227         asm("retl");
228         asm("nop");
229 }
230 #endif   /* __sparc || __sparc__ */
231
232
233
234
235 #if defined(NEED_I386_TAS_ASM)
236 /* non gcc i386 based things */
237 #endif   /* NEED_I386_TAS_ASM */
238 #endif   /* not __GNUC__ */
239
240
241
242
243 /*****************************************************************************/
244 #if defined(S_LOCK_TEST)
245
246 /*
247  * test program for verifying a port's spinlock support.
248  */
249
250 volatile slock_t test_lock;
251
252 int
253 main()
254 {
255         srandom((unsigned int) time(NULL));
256
257         S_INIT_LOCK(&test_lock);
258
259         if (!S_LOCK_FREE(&test_lock))
260         {
261                 printf("S_LOCK_TEST: failed, lock not initialized\n");
262                 return 1;
263         }
264
265         S_LOCK(&test_lock);
266
267         if (S_LOCK_FREE(&test_lock))
268         {
269                 printf("S_LOCK_TEST: failed, lock not locked\n");
270                 return 1;
271         }
272
273         S_UNLOCK(&test_lock);
274
275         if (!S_LOCK_FREE(&test_lock))
276         {
277                 printf("S_LOCK_TEST: failed, lock not unlocked\n");
278                 return 1;
279         }
280
281         S_LOCK(&test_lock);
282
283         if (S_LOCK_FREE(&test_lock))
284         {
285                 printf("S_LOCK_TEST: failed, lock not re-locked\n");
286                 return 1;
287         }
288
289         printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS);
290         printf("             exit with a 'stuck spinlock' message\n");
291         printf("             if S_LOCK() and TAS() are working.\n");
292         fflush(stdout);
293
294         s_lock(&test_lock, __FILE__, __LINE__);
295
296         printf("S_LOCK_TEST: failed, lock not locked\n");
297         return 1;
298 }
299
300 #endif   /* S_LOCK_TEST */