~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/storage/lmgr/s_lock.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.35 2004-12-31 22:01:05 pgsql 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
#if defined(__mips__) && !defined(__sgi)
 
176
static void
 
177
tas_dummy()
 
178
{
 
179
        __asm__         __volatile__(
 
180
                                                                                 "\
 
181
.global tas                                             \n\
 
182
tas:                                                    \n\
 
183
                        .frame  $sp, 0, $31     \n\
 
184
                        .set push               \n\
 
185
                        .set mips2              \n\
 
186
                        ll              $14, 0($4)      \n\
 
187
                        or              $15, $14, 1     \n\
 
188
                        sc              $15, 0($4)      \n\
 
189
                        .set pop                        \n\
 
190
                        beq             $15, 0, fail\n\
 
191
                        bne             $14, 0, fail\n\
 
192
                        li              $2, 0           \n\
 
193
                        .livereg 0x2000FF0E,0x00000FFF  \n\
 
194
                        j               $31                     \n\
 
195
fail:                                                   \n\
 
196
                        li              $2, 1           \n\
 
197
                        j       $31                     \n\
 
198
");
 
199
}
 
200
#endif   /* __mips__ && !__sgi */
 
201
 
 
202
 
 
203
#else                                                   /* not __GNUC__ */
 
204
 
 
205
/*
 
206
 * All non gcc
 
207
 */
 
208
 
 
209
 
 
210
#if defined(sun3)
 
211
static void
 
212
tas_dummy()                                             /* really means: extern int tas(slock_t
 
213
                                                                 * *lock); */
 
214
{
 
215
        asm("LLA0:");
 
216
        asm("   .data");
 
217
        asm("   .text");
 
218
        asm("|#PROC# 04");
 
219
        asm("   .globl  _tas");
 
220
        asm("_tas:");
 
221
        asm("|#PROLOGUE# 1");
 
222
        asm("   movel   sp@(0x4),a0");
 
223
        asm("   tas a0@");
 
224
        asm("   beq LLA1");
 
225
        asm("   moveq   #-128,d0");
 
226
        asm("   rts");
 
227
        asm("LLA1:");
 
228
        asm("   moveq   #0,d0");
 
229
        asm("   rts");
 
230
        asm("   .data");
 
231
}
 
232
#endif   /* sun3 */
 
233
 
 
234
 
 
235
#if defined(__sparc__) || defined(__sparc)
 
236
/*
 
237
 * sparc machines not using gcc
 
238
 */
 
239
static void
 
240
tas_dummy()                                             /* really means: extern int tas(slock_t
 
241
                                                                 * *lock); */
 
242
{
 
243
 
 
244
#ifdef SUNOS4_CC
 
245
        asm(".seg \"data\"");
 
246
        asm(".seg \"text\"");
 
247
#else
 
248
        asm(".section \"data\"");
 
249
        asm(".section \"text\"");
 
250
#endif
 
251
 
 
252
        asm("_tas:");
 
253
 
 
254
        /*
 
255
         * Sparc atomic test and set (sparc calls it "atomic load-store")
 
256
         */
 
257
        asm("ldstub [%r8], %r8");
 
258
        asm("retl");
 
259
        asm("nop");
 
260
}
 
261
#endif   /* __sparc || __sparc__ */
 
262
#endif   /* not __GNUC__ */
 
263
#endif   /* HAVE_SPINLOCKS */
 
264
 
 
265
 
 
266
 
 
267
/*****************************************************************************/
 
268
#if defined(S_LOCK_TEST)
 
269
 
 
270
/*
 
271
 * test program for verifying a port's spinlock support.
 
272
 */
 
273
 
 
274
struct test_lock_struct
 
275
{
 
276
        char            pad1;
 
277
        slock_t         lock;
 
278
        char            pad2;
 
279
};
 
280
 
 
281
volatile struct test_lock_struct test_lock;
 
282
 
 
283
int
 
284
main()
 
285
{
 
286
        srandom((unsigned int) time(NULL));
 
287
 
 
288
        test_lock.pad1 = test_lock.pad2 = 0x44;
 
289
 
 
290
        S_INIT_LOCK(&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 initialized\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 locked\n");
 
315
                return 1;
 
316
        }
 
317
 
 
318
        S_UNLOCK(&test_lock.lock);
 
319
 
 
320
        if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
 
321
        {
 
322
                printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
 
323
                return 1;
 
324
        }
 
325
 
 
326
        if (!S_LOCK_FREE(&test_lock.lock))
 
327
        {
 
328
                printf("S_LOCK_TEST: failed, lock not unlocked\n");
 
329
                return 1;
 
330
        }
 
331
 
 
332
        S_LOCK(&test_lock.lock);
 
333
 
 
334
        if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
 
335
        {
 
336
                printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
 
337
                return 1;
 
338
        }
 
339
 
 
340
        if (S_LOCK_FREE(&test_lock.lock))
 
341
        {
 
342
                printf("S_LOCK_TEST: failed, lock not re-locked\n");
 
343
                return 1;
 
344
        }
 
345
 
 
346
        printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS);
 
347
        printf("             exit with a 'stuck spinlock' message\n");
 
348
        printf("             if S_LOCK() and TAS() are working.\n");
 
349
        fflush(stdout);
 
350
 
 
351
        s_lock(&test_lock.lock, __FILE__, __LINE__);
 
352
 
 
353
        printf("S_LOCK_TEST: failed, lock not locked\n");
 
354
        return 1;
 
355
}
 
356
 
 
357
#endif   /* S_LOCK_TEST */