~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/port/posix_sema.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
 * posix_sema.c
 
4
 *        Implement PGSemaphores using POSIX semaphore facilities
 
5
 *
 
6
 * We prefer the unnamed style of POSIX semaphore (the kind made with
 
7
 * sem_init).  We can cope with the kind made with sem_open, however.
 
8
 *
 
9
 *
 
10
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
11
 * Portions Copyright (c) 1994, Regents of the University of California
 
12
 *
 
13
 * IDENTIFICATION
 
14
 *        $PostgreSQL: pgsql/src/backend/port/posix_sema.c,v 1.13 2004-12-31 22:00:29 pgsql Exp $
 
15
 *
 
16
 *-------------------------------------------------------------------------
 
17
 */
 
18
#include "postgres.h"
 
19
 
 
20
#include <errno.h>
 
21
#include <fcntl.h>
 
22
#include <signal.h>
 
23
#include <unistd.h>
 
24
 
 
25
#include "miscadmin.h"
 
26
#include "storage/ipc.h"
 
27
#include "storage/pg_sema.h"
 
28
 
 
29
 
 
30
#ifdef USE_NAMED_POSIX_SEMAPHORES
 
31
/* PGSemaphore is pointer to pointer to sem_t */
 
32
#define PG_SEM_REF(x)   (*(x))
 
33
#else
 
34
/* PGSemaphore is pointer to sem_t */
 
35
#define PG_SEM_REF(x)   (x)
 
36
#endif
 
37
 
 
38
 
 
39
#define IPCProtection   (0600)  /* access/modify by user only */
 
40
 
 
41
static sem_t **mySemPointers;   /* keep track of created semaphores */
 
42
static int      numSems;                        /* number of semas acquired so far */
 
43
static int      maxSems;                        /* allocated size of mySemaPointers array */
 
44
static int      nextSemKey;                     /* next name to try */
 
45
 
 
46
 
 
47
static void ReleaseSemaphores(int status, Datum arg);
 
48
 
 
49
 
 
50
#ifdef USE_NAMED_POSIX_SEMAPHORES
 
51
 
 
52
/*
 
53
 * PosixSemaphoreCreate
 
54
 *
 
55
 * Attempt to create a new named semaphore.
 
56
 *
 
57
 * If we fail with a failure code other than collision-with-existing-sema,
 
58
 * print out an error and abort.  Other types of errors suggest nonrecoverable
 
59
 * problems.
 
60
 */
 
61
static sem_t *
 
62
PosixSemaphoreCreate(void)
 
63
{
 
64
        int                     semKey;
 
65
        char            semname[64];
 
66
        sem_t      *mySem;
 
67
 
 
68
        for (;;)
 
69
        {
 
70
                semKey = nextSemKey++;
 
71
 
 
72
                snprintf(semname, sizeof(semname), "/pgsql-%d", semKey);
 
73
 
 
74
                mySem = sem_open(semname, O_CREAT | O_EXCL,
 
75
                                                 (mode_t) IPCProtection, (unsigned) 1);
 
76
 
 
77
#ifdef SEM_FAILED
 
78
                if (mySem != (sem_t *) SEM_FAILED)
 
79
                        break;
 
80
#else
 
81
                if (mySem != (sem_t *) (-1))
 
82
                        break;
 
83
#endif
 
84
 
 
85
                /* Loop if error indicates a collision */
 
86
                if (errno == EEXIST || errno == EACCES || errno == EINTR)
 
87
                        continue;
 
88
 
 
89
                /*
 
90
                 * Else complain and abort
 
91
                 */
 
92
                elog(FATAL, "sem_open(\"%s\") failed: %m", semname);
 
93
        }
 
94
 
 
95
        /*
 
96
         * Unlink the semaphore immediately, so it can't be accessed
 
97
         * externally. This also ensures that it will go away if we crash.
 
98
         */
 
99
        sem_unlink(semname);
 
100
 
 
101
        return mySem;
 
102
}
 
103
 
 
104
#else                                                   /* !USE_NAMED_POSIX_SEMAPHORES */
 
105
 
 
106
/*
 
107
 * PosixSemaphoreCreate
 
108
 *
 
109
 * Attempt to create a new unnamed semaphore.
 
110
 */
 
111
static void
 
112
PosixSemaphoreCreate(sem_t * sem)
 
113
{
 
114
        if (sem_init(sem, 1, 1) < 0)
 
115
                elog(FATAL, "sem_init failed: %m");
 
116
}
 
117
#endif   /* USE_NAMED_POSIX_SEMAPHORES */
 
118
 
 
119
 
 
120
/*
 
121
 * PosixSemaphoreKill   - removes a semaphore
 
122
 */
 
123
static void
 
124
PosixSemaphoreKill(sem_t * sem)
 
125
{
 
126
#ifdef USE_NAMED_POSIX_SEMAPHORES
 
127
        /* Got to use sem_close for named semaphores */
 
128
        if (sem_close(sem) < 0)
 
129
                elog(LOG, "sem_close failed: %m");
 
130
#else
 
131
        /* Got to use sem_destroy for unnamed semaphores */
 
132
        if (sem_destroy(sem) < 0)
 
133
                elog(LOG, "sem_destroy failed: %m");
 
134
#endif
 
135
}
 
136
 
 
137
 
 
138
/*
 
139
 * PGReserveSemaphores --- initialize semaphore support
 
140
 *
 
141
 * This is called during postmaster start or shared memory reinitialization.
 
142
 * It should do whatever is needed to be able to support up to maxSemas
 
143
 * subsequent PGSemaphoreCreate calls.  Also, if any system resources
 
144
 * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
 
145
 * callback to release them.
 
146
 *
 
147
 * The port number is passed for possible use as a key (for Posix, we use
 
148
 * it to generate the starting semaphore name).  In a standalone backend,
 
149
 * zero will be passed.
 
150
 *
 
151
 * In the Posix implementation, we acquire semaphores on-demand; the
 
152
 * maxSemas parameter is just used to size the array that keeps track of
 
153
 * acquired semas for subsequent releasing.
 
154
 */
 
155
void
 
156
PGReserveSemaphores(int maxSemas, int port)
 
157
{
 
158
        mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
 
159
        if (mySemPointers == NULL)
 
160
                elog(PANIC, "out of memory");
 
161
        numSems = 0;
 
162
        maxSems = maxSemas;
 
163
        nextSemKey = port * 1000;
 
164
 
 
165
        on_shmem_exit(ReleaseSemaphores, 0);
 
166
}
 
167
 
 
168
/*
 
169
 * Release semaphores at shutdown or shmem reinitialization
 
170
 *
 
171
 * (called as an on_shmem_exit callback, hence funny argument list)
 
172
 */
 
173
static void
 
174
ReleaseSemaphores(int status, Datum arg)
 
175
{
 
176
        int                     i;
 
177
 
 
178
        for (i = 0; i < numSems; i++)
 
179
                PosixSemaphoreKill(mySemPointers[i]);
 
180
        free(mySemPointers);
 
181
}
 
182
 
 
183
/*
 
184
 * PGSemaphoreCreate
 
185
 *
 
186
 * Initialize a PGSemaphore structure to represent a sema with count 1
 
187
 */
 
188
void
 
189
PGSemaphoreCreate(PGSemaphore sema)
 
190
{
 
191
        sem_t      *newsem;
 
192
 
 
193
        /* Can't do this in a backend, because static state is postmaster's */
 
194
        Assert(!IsUnderPostmaster);
 
195
 
 
196
        if (numSems >= maxSems)
 
197
                elog(PANIC, "too many semaphores created");
 
198
 
 
199
#ifdef USE_NAMED_POSIX_SEMAPHORES
 
200
        *sema = newsem = PosixSemaphoreCreate();
 
201
#else
 
202
        PosixSemaphoreCreate(sema);
 
203
        newsem = sema;
 
204
#endif
 
205
 
 
206
        /* Remember new sema for ReleaseSemaphores */
 
207
        mySemPointers[numSems++] = newsem;
 
208
}
 
209
 
 
210
/*
 
211
 * PGSemaphoreReset
 
212
 *
 
213
 * Reset a previously-initialized PGSemaphore to have count 0
 
214
 */
 
215
void
 
216
PGSemaphoreReset(PGSemaphore sema)
 
217
{
 
218
        /*
 
219
         * There's no direct API for this in POSIX, so we have to ratchet the
 
220
         * semaphore down to 0 with repeated trywait's.
 
221
         */
 
222
        for (;;)
 
223
        {
 
224
                if (sem_trywait(PG_SEM_REF(sema)) < 0)
 
225
                {
 
226
                        if (errno == EAGAIN || errno == EDEADLK)
 
227
                                break;                  /* got it down to 0 */
 
228
                        if (errno == EINTR)
 
229
                                continue;               /* can this happen? */
 
230
                        elog(FATAL, "sem_trywait failed: %m");
 
231
                }
 
232
        }
 
233
}
 
234
 
 
235
/*
 
236
 * PGSemaphoreLock
 
237
 *
 
238
 * Lock a semaphore (decrement count), blocking if count would be < 0
 
239
 */
 
240
void
 
241
PGSemaphoreLock(PGSemaphore sema, bool interruptOK)
 
242
{
 
243
        int                     errStatus;
 
244
 
 
245
        /*
 
246
         * Note: if errStatus is -1 and errno == EINTR then it means we
 
247
         * returned from the operation prematurely because we were sent a
 
248
         * signal.      So we try and lock the semaphore again.
 
249
         *
 
250
         * Each time around the loop, we check for a cancel/die interrupt. We
 
251
         * assume that if such an interrupt comes in while we are waiting, it
 
252
         * will cause the sem_wait() call to exit with errno == EINTR, so that
 
253
         * we will be able to service the interrupt (if not in a critical
 
254
         * section already).
 
255
         *
 
256
         * Once we acquire the lock, we do NOT check for an interrupt before
 
257
         * returning.  The caller needs to be able to record ownership of the
 
258
         * lock before any interrupt can be accepted.
 
259
         *
 
260
         * There is a window of a few instructions between CHECK_FOR_INTERRUPTS
 
261
         * and entering the sem_wait() call.  If a cancel/die interrupt occurs
 
262
         * in that window, we would fail to notice it until after we acquire
 
263
         * the lock (or get another interrupt to escape the sem_wait()).  We
 
264
         * can avoid this problem by temporarily setting ImmediateInterruptOK
 
265
         * to true before we do CHECK_FOR_INTERRUPTS; then, a die() interrupt
 
266
         * in this interval will execute directly.      However, there is a huge
 
267
         * pitfall: there is another window of a few instructions after the
 
268
         * sem_wait() before we are able to reset ImmediateInterruptOK.  If an
 
269
         * interrupt occurs then, we'll lose control, which means that the
 
270
         * lock has been acquired but our caller did not get a chance to
 
271
         * record the fact. Therefore, we only set ImmediateInterruptOK if the
 
272
         * caller tells us it's OK to do so, ie, the caller does not need to
 
273
         * record acquiring the lock.  (This is currently true for lockmanager
 
274
         * locks, since the process that granted us the lock did all the
 
275
         * necessary state updates. It's not true for Posix semaphores used to
 
276
         * implement LW locks or emulate spinlocks --- but the wait time for
 
277
         * such locks should not be very long, anyway.)
 
278
         */
 
279
        do
 
280
        {
 
281
                ImmediateInterruptOK = interruptOK;
 
282
                CHECK_FOR_INTERRUPTS();
 
283
                errStatus = sem_wait(PG_SEM_REF(sema));
 
284
                ImmediateInterruptOK = false;
 
285
        } while (errStatus < 0 && errno == EINTR);
 
286
 
 
287
        if (errStatus < 0)
 
288
                elog(FATAL, "sem_wait failed: %m");
 
289
}
 
290
 
 
291
/*
 
292
 * PGSemaphoreUnlock
 
293
 *
 
294
 * Unlock a semaphore (increment count)
 
295
 */
 
296
void
 
297
PGSemaphoreUnlock(PGSemaphore sema)
 
298
{
 
299
        int                     errStatus;
 
300
 
 
301
        /*
 
302
         * Note: if errStatus is -1 and errno == EINTR then it means we
 
303
         * returned from the operation prematurely because we were sent a
 
304
         * signal.      So we try and unlock the semaphore again. Not clear this
 
305
         * can really happen, but might as well cope.
 
306
         */
 
307
        do
 
308
        {
 
309
                errStatus = sem_post(PG_SEM_REF(sema));
 
310
        } while (errStatus < 0 && errno == EINTR);
 
311
 
 
312
        if (errStatus < 0)
 
313
                elog(FATAL, "sem_post failed: %m");
 
314
}
 
315
 
 
316
/*
 
317
 * PGSemaphoreTryLock
 
318
 *
 
319
 * Lock a semaphore only if able to do so without blocking
 
320
 */
 
321
bool
 
322
PGSemaphoreTryLock(PGSemaphore sema)
 
323
{
 
324
        int                     errStatus;
 
325
 
 
326
        /*
 
327
         * Note: if errStatus is -1 and errno == EINTR then it means we
 
328
         * returned from the operation prematurely because we were sent a
 
329
         * signal.      So we try and lock the semaphore again.
 
330
         */
 
331
        do
 
332
        {
 
333
                errStatus = sem_trywait(PG_SEM_REF(sema));
 
334
        } while (errStatus < 0 && errno == EINTR);
 
335
 
 
336
        if (errStatus < 0)
 
337
        {
 
338
                if (errno == EAGAIN || errno == EDEADLK)
 
339
                        return false;           /* failed to lock it */
 
340
                /* Otherwise we got trouble */
 
341
                elog(FATAL, "sem_trywait failed: %m");
 
342
        }
 
343
 
 
344
        return true;
 
345
}