OSDN Git Service

pgindent run.
[pg-rex/syncrep.git] / src / backend / port / posix_sema.c
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-2002, PostgreSQL Global Development Group
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  * IDENTIFICATION
14  *        $Header: /cvsroot/pgsql/src/backend/port/posix_sema.c,v 1.6 2002/09/04 20:31:24 momjian 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                 fprintf(stderr, "PosixSemaphoreCreate: sem_open(\"%s\") failed: %s\n",
93                                 semname, strerror(errno));
94                 proc_exit(1);
95         }
96
97         /*
98          * Unlink the semaphore immediately, so it can't be accessed
99          * externally. This also ensures that it will go away if we crash.
100          */
101         sem_unlink(semname);
102
103         return mySem;
104 }
105
106 #else                                                   /* !USE_NAMED_POSIX_SEMAPHORES */
107
108 /*
109  * PosixSemaphoreCreate
110  *
111  * Attempt to create a new unnamed semaphore.
112  */
113 static void
114 PosixSemaphoreCreate(sem_t * sem)
115 {
116         if (sem_init(sem, 1, 1) < 0)
117         {
118                 fprintf(stderr, "PosixSemaphoreCreate: sem_init failed: %s\n",
119                                 strerror(errno));
120                 proc_exit(1);
121         }
122 }
123 #endif   /* USE_NAMED_POSIX_SEMAPHORES */
124
125
126 /*
127  * PosixSemaphoreKill   - removes a semaphore
128  */
129 static void
130 PosixSemaphoreKill(sem_t * sem)
131 {
132 #ifdef USE_NAMED_POSIX_SEMAPHORES
133         /* Got to use sem_close for named semaphores */
134         if (sem_close(sem) < 0)
135                 fprintf(stderr, "PosixSemaphoreKill: sem_close failed: %s\n",
136                                 strerror(errno));
137 #else
138         /* Got to use sem_destroy for unnamed semaphores */
139         if (sem_destroy(sem) < 0)
140                 fprintf(stderr, "PosixSemaphoreKill: sem_destroy failed: %s\n",
141                                 strerror(errno));
142 #endif
143 }
144
145
146 /*
147  * PGReserveSemaphores --- initialize semaphore support
148  *
149  * This is called during postmaster start or shared memory reinitialization.
150  * It should do whatever is needed to be able to support up to maxSemas
151  * subsequent PGSemaphoreCreate calls.  Also, if any system resources
152  * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
153  * callback to release them.
154  *
155  * The port number is passed for possible use as a key (for Posix, we use
156  * it to generate the starting semaphore name).  In a standalone backend,
157  * zero will be passed.
158  *
159  * In the Posix implementation, we acquire semaphores on-demand; the
160  * maxSemas parameter is just used to size the array that keeps track of
161  * acquired semas for subsequent releasing.
162  */
163 void
164 PGReserveSemaphores(int maxSemas, int port)
165 {
166         mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
167         if (mySemPointers == NULL)
168                 elog(PANIC, "Out of memory in PGReserveSemaphores");
169         numSems = 0;
170         maxSems = maxSemas;
171         nextSemKey = port * 1000;
172
173         on_shmem_exit(ReleaseSemaphores, 0);
174 }
175
176 /*
177  * Release semaphores at shutdown or shmem reinitialization
178  *
179  * (called as an on_shmem_exit callback, hence funny argument list)
180  */
181 static void
182 ReleaseSemaphores(int status, Datum arg)
183 {
184         int                     i;
185
186         for (i = 0; i < numSems; i++)
187                 PosixSemaphoreKill(mySemPointers[i]);
188         free(mySemPointers);
189 }
190
191 /*
192  * PGSemaphoreCreate
193  *
194  * Initialize a PGSemaphore structure to represent a sema with count 1
195  */
196 void
197 PGSemaphoreCreate(PGSemaphore sema)
198 {
199         sem_t      *newsem;
200
201         /* Can't do this in a backend, because static state is postmaster's */
202         Assert(!IsUnderPostmaster);
203
204         if (numSems >= maxSems)
205                 elog(PANIC, "PGSemaphoreCreate: too many semaphores created");
206
207 #ifdef USE_NAMED_POSIX_SEMAPHORES
208         *sema = newsem = PosixSemaphoreCreate();
209 #else
210         PosixSemaphoreCreate(sema);
211         newsem = sema;
212 #endif
213
214         /* Remember new sema for ReleaseSemaphores */
215         mySemPointers[numSems++] = newsem;
216 }
217
218 /*
219  * PGSemaphoreReset
220  *
221  * Reset a previously-initialized PGSemaphore to have count 0
222  */
223 void
224 PGSemaphoreReset(PGSemaphore sema)
225 {
226         /*
227          * There's no direct API for this in POSIX, so we have to ratchet the
228          * semaphore down to 0 with repeated trywait's.
229          */
230         for (;;)
231         {
232                 if (sem_trywait(PG_SEM_REF(sema)) < 0)
233                 {
234                         if (errno == EAGAIN || errno == EDEADLK)
235                                 break;                  /* got it down to 0 */
236                         if (errno == EINTR)
237                                 continue;               /* can this happen? */
238                         fprintf(stderr, "PGSemaphoreReset: sem_trywait failed: %s\n",
239                                         strerror(errno));
240                         proc_exit(1);
241                 }
242         }
243 }
244
245 /*
246  * PGSemaphoreLock
247  *
248  * Lock a semaphore (decrement count), blocking if count would be < 0
249  */
250 void
251 PGSemaphoreLock(PGSemaphore sema, bool interruptOK)
252 {
253         int                     errStatus;
254
255         /*
256          * Note: if errStatus is -1 and errno == EINTR then it means we
257          * returned from the operation prematurely because we were sent a
258          * signal.      So we try and lock the semaphore again.
259          *
260          * Each time around the loop, we check for a cancel/die interrupt. We
261          * assume that if such an interrupt comes in while we are waiting, it
262          * will cause the sem_wait() call to exit with errno == EINTR, so that
263          * we will be able to service the interrupt (if not in a critical
264          * section already).
265          *
266          * Once we acquire the lock, we do NOT check for an interrupt before
267          * returning.  The caller needs to be able to record ownership of the
268          * lock before any interrupt can be accepted.
269          *
270          * There is a window of a few instructions between CHECK_FOR_INTERRUPTS
271          * and entering the sem_wait() call.  If a cancel/die interrupt occurs
272          * in that window, we would fail to notice it until after we acquire
273          * the lock (or get another interrupt to escape the sem_wait()).  We
274          * can avoid this problem by temporarily setting ImmediateInterruptOK
275          * to true before we do CHECK_FOR_INTERRUPTS; then, a die() interrupt
276          * in this interval will execute directly.      However, there is a huge
277          * pitfall: there is another window of a few instructions after the
278          * sem_wait() before we are able to reset ImmediateInterruptOK.  If an
279          * interrupt occurs then, we'll lose control, which means that the
280          * lock has been acquired but our caller did not get a chance to
281          * record the fact. Therefore, we only set ImmediateInterruptOK if the
282          * caller tells us it's OK to do so, ie, the caller does not need to
283          * record acquiring the lock.  (This is currently true for lockmanager
284          * locks, since the process that granted us the lock did all the
285          * necessary state updates. It's not true for Posix semaphores used to
286          * implement LW locks or emulate spinlocks --- but the wait time for
287          * such locks should not be very long, anyway.)
288          */
289         do
290         {
291                 ImmediateInterruptOK = interruptOK;
292                 CHECK_FOR_INTERRUPTS();
293                 errStatus = sem_wait(PG_SEM_REF(sema));
294                 ImmediateInterruptOK = false;
295         } while (errStatus < 0 && errno == EINTR);
296
297         if (errStatus < 0)
298         {
299                 fprintf(stderr, "PGSemaphoreLock: sem_wait failed: %s\n",
300                                 strerror(errno));
301                 proc_exit(255);
302         }
303 }
304
305 /*
306  * PGSemaphoreUnlock
307  *
308  * Unlock a semaphore (increment count)
309  */
310 void
311 PGSemaphoreUnlock(PGSemaphore sema)
312 {
313         int                     errStatus;
314
315         /*
316          * Note: if errStatus is -1 and errno == EINTR then it means we
317          * returned from the operation prematurely because we were sent a
318          * signal.      So we try and unlock the semaphore again. Not clear this
319          * can really happen, but might as well cope.
320          */
321         do
322         {
323                 errStatus = sem_post(PG_SEM_REF(sema));
324         } while (errStatus < 0 && errno == EINTR);
325
326         if (errStatus < 0)
327         {
328                 fprintf(stderr, "PGSemaphoreUnlock: sem_post failed: %s\n",
329                                 strerror(errno));
330                 proc_exit(255);
331         }
332 }
333
334 /*
335  * PGSemaphoreTryLock
336  *
337  * Lock a semaphore only if able to do so without blocking
338  */
339 bool
340 PGSemaphoreTryLock(PGSemaphore sema)
341 {
342         int                     errStatus;
343
344         /*
345          * Note: if errStatus is -1 and errno == EINTR then it means we
346          * returned from the operation prematurely because we were sent a
347          * signal.      So we try and lock the semaphore again.
348          */
349         do
350         {
351                 errStatus = sem_trywait(PG_SEM_REF(sema));
352         } while (errStatus < 0 && errno == EINTR);
353
354         if (errStatus < 0)
355         {
356                 if (errno == EAGAIN || errno == EDEADLK)
357                         return false;           /* failed to lock it */
358                 /* Otherwise we got trouble */
359                 fprintf(stderr, "PGSemaphoreTryLock: sem_trywait failed: %s\n",
360                                 strerror(errno));
361                 proc_exit(255);
362         }
363
364         return true;
365 }