Line data Source code
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 : * In either implementation, typedef PGSemaphore is equivalent to "sem_t *".
10 : * With unnamed semaphores, the sem_t structs live in an array in shared
11 : * memory. With named semaphores, that's not true because we cannot persuade
12 : * sem_open to do its allocation there. Therefore, the named-semaphore code
13 : * *does not cope with EXEC_BACKEND*. The sem_t structs will just be in the
14 : * postmaster's private memory, where they are successfully inherited by
15 : * forked backends, but they could not be accessed by exec'd backends.
16 : *
17 : *
18 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
19 : * Portions Copyright (c) 1994, Regents of the University of California
20 : *
21 : * IDENTIFICATION
22 : * src/backend/port/posix_sema.c
23 : *
24 : *-------------------------------------------------------------------------
25 : */
26 : #include "postgres.h"
27 :
28 : #include <fcntl.h>
29 : #include <semaphore.h>
30 : #include <signal.h>
31 : #include <unistd.h>
32 :
33 : #include "miscadmin.h"
34 : #include "storage/ipc.h"
35 : #include "storage/pg_sema.h"
36 : #include "storage/shmem.h"
37 :
38 :
39 : /* see file header comment */
40 : #if defined(USE_NAMED_POSIX_SEMAPHORES) && defined(EXEC_BACKEND)
41 : #error cannot use named POSIX semaphores with EXEC_BACKEND
42 : #endif
43 :
44 : /* typedef PGSemaphore is equivalent to pointer to sem_t */
45 : typedef struct PGSemaphoreData
46 : {
47 : sem_t pgsem;
48 : } PGSemaphoreData;
49 :
50 : #define PG_SEM_REF(x) (&(x)->pgsem)
51 :
52 : #define IPCProtection (0600) /* access/modify by user only */
53 :
54 : #ifdef USE_NAMED_POSIX_SEMAPHORES
55 : static sem_t **mySemPointers; /* keep track of created semaphores */
56 : #else
57 : static PGSemaphore sharedSemas; /* array of PGSemaphoreData in shared memory */
58 : #endif
59 : static int numSems; /* number of semas acquired so far */
60 : static int maxSems; /* allocated size of above arrays */
61 : static int nextSemKey; /* next name to try */
62 :
63 :
64 : static void ReleaseSemaphores(int status, Datum arg);
65 :
66 :
67 : #ifdef USE_NAMED_POSIX_SEMAPHORES
68 :
69 : /*
70 : * PosixSemaphoreCreate
71 : *
72 : * Attempt to create a new named semaphore.
73 : *
74 : * If we fail with a failure code other than collision-with-existing-sema,
75 : * print out an error and abort. Other types of errors suggest nonrecoverable
76 : * problems.
77 : */
78 : static sem_t *
79 : PosixSemaphoreCreate(void)
80 : {
81 : int semKey;
82 : char semname[64];
83 : sem_t *mySem;
84 :
85 : for (;;)
86 : {
87 : semKey = nextSemKey++;
88 :
89 : snprintf(semname, sizeof(semname), "/pgsql-%d", semKey);
90 :
91 : mySem = sem_open(semname, O_CREAT | O_EXCL,
92 : (mode_t) IPCProtection, (unsigned) 1);
93 :
94 : #ifdef SEM_FAILED
95 : if (mySem != (sem_t *) SEM_FAILED)
96 : break;
97 : #else
98 : if (mySem != (sem_t *) (-1))
99 : break;
100 : #endif
101 :
102 : /* Loop if error indicates a collision */
103 : if (errno == EEXIST || errno == EACCES || errno == EINTR)
104 : continue;
105 :
106 : /*
107 : * Else complain and abort
108 : */
109 : elog(FATAL, "sem_open(\"%s\") failed: %m", semname);
110 : }
111 :
112 : /*
113 : * Unlink the semaphore immediately, so it can't be accessed externally.
114 : * This also ensures that it will go away if we crash.
115 : */
116 : sem_unlink(semname);
117 :
118 : return mySem;
119 : }
120 : #else /* !USE_NAMED_POSIX_SEMAPHORES */
121 :
122 : /*
123 : * PosixSemaphoreCreate
124 : *
125 : * Attempt to create a new unnamed semaphore.
126 : */
127 : static void
128 580 : PosixSemaphoreCreate(sem_t *sem)
129 : {
130 580 : if (sem_init(sem, 1, 1) < 0)
131 0 : elog(FATAL, "sem_init failed: %m");
132 580 : }
133 : #endif /* USE_NAMED_POSIX_SEMAPHORES */
134 :
135 :
136 : /*
137 : * PosixSemaphoreKill - removes a semaphore
138 : */
139 : static void
140 580 : PosixSemaphoreKill(sem_t *sem)
141 : {
142 : #ifdef USE_NAMED_POSIX_SEMAPHORES
143 : /* Got to use sem_close for named semaphores */
144 : if (sem_close(sem) < 0)
145 : elog(LOG, "sem_close failed: %m");
146 : #else
147 : /* Got to use sem_destroy for unnamed semaphores */
148 580 : if (sem_destroy(sem) < 0)
149 0 : elog(LOG, "sem_destroy failed: %m");
150 : #endif
151 580 : }
152 :
153 :
154 : /*
155 : * Report amount of shared memory needed for semaphores
156 : */
157 : Size
158 10 : PGSemaphoreShmemSize(int maxSemas)
159 : {
160 : #ifdef USE_NAMED_POSIX_SEMAPHORES
161 : /* No shared memory needed in this case */
162 : return 0;
163 : #else
164 : /* Need a PGSemaphoreData per semaphore */
165 10 : return mul_size(maxSemas, sizeof(PGSemaphoreData));
166 : #endif
167 : }
168 :
169 : /*
170 : * PGReserveSemaphores --- initialize semaphore support
171 : *
172 : * This is called during postmaster start or shared memory reinitialization.
173 : * It should do whatever is needed to be able to support up to maxSemas
174 : * subsequent PGSemaphoreCreate calls. Also, if any system resources
175 : * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
176 : * callback to release them.
177 : *
178 : * The port number is passed for possible use as a key (for Posix, we use
179 : * it to generate the starting semaphore name). In a standalone backend,
180 : * zero will be passed.
181 : *
182 : * In the Posix implementation, we acquire semaphores on-demand; the
183 : * maxSemas parameter is just used to size the arrays. For unnamed
184 : * semaphores, there is an array of PGSemaphoreData structs in shared memory.
185 : * For named semaphores, we keep a postmaster-local array of sem_t pointers,
186 : * which we use for releasing the semphores when done.
187 : * (This design minimizes the dependency of postmaster shutdown on the
188 : * contents of shared memory, which a failed backend might have clobbered.
189 : * We can't do much about the possibility of sem_destroy() crashing, but
190 : * we don't have to expose the counters to other processes.)
191 : */
192 : void
193 5 : PGReserveSemaphores(int maxSemas, int port)
194 : {
195 : #ifdef USE_NAMED_POSIX_SEMAPHORES
196 : mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
197 : if (mySemPointers == NULL)
198 : elog(PANIC, "out of memory");
199 : #else
200 :
201 : /*
202 : * We must use ShmemAllocUnlocked(), since the spinlock protecting
203 : * ShmemAlloc() won't be ready yet. (This ordering is necessary when we
204 : * are emulating spinlocks with semaphores.)
205 : */
206 5 : sharedSemas = (PGSemaphore)
207 5 : ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
208 : #endif
209 :
210 5 : numSems = 0;
211 5 : maxSems = maxSemas;
212 5 : nextSemKey = port * 1000;
213 :
214 5 : on_shmem_exit(ReleaseSemaphores, 0);
215 5 : }
216 :
217 : /*
218 : * Release semaphores at shutdown or shmem reinitialization
219 : *
220 : * (called as an on_shmem_exit callback, hence funny argument list)
221 : */
222 : static void
223 5 : ReleaseSemaphores(int status, Datum arg)
224 : {
225 : int i;
226 :
227 : #ifdef USE_NAMED_POSIX_SEMAPHORES
228 : for (i = 0; i < numSems; i++)
229 : PosixSemaphoreKill(mySemPointers[i]);
230 : free(mySemPointers);
231 : #endif
232 :
233 : #ifdef USE_UNNAMED_POSIX_SEMAPHORES
234 585 : for (i = 0; i < numSems; i++)
235 580 : PosixSemaphoreKill(PG_SEM_REF(sharedSemas + i));
236 : #endif
237 5 : }
238 :
239 : /*
240 : * PGSemaphoreCreate
241 : *
242 : * Allocate a PGSemaphore structure with initial count 1
243 : */
244 : PGSemaphore
245 580 : PGSemaphoreCreate(void)
246 : {
247 : PGSemaphore sema;
248 : sem_t *newsem;
249 :
250 : /* Can't do this in a backend, because static state is postmaster's */
251 580 : Assert(!IsUnderPostmaster);
252 :
253 580 : if (numSems >= maxSems)
254 0 : elog(PANIC, "too many semaphores created");
255 :
256 : #ifdef USE_NAMED_POSIX_SEMAPHORES
257 : newsem = PosixSemaphoreCreate();
258 : /* Remember new sema for ReleaseSemaphores */
259 : mySemPointers[numSems] = newsem;
260 : sema = (PGSemaphore) newsem;
261 : #else
262 580 : sema = &sharedSemas[numSems];
263 580 : newsem = PG_SEM_REF(sema);
264 580 : PosixSemaphoreCreate(newsem);
265 : #endif
266 :
267 580 : numSems++;
268 :
269 580 : return sema;
270 : }
271 :
272 : /*
273 : * PGSemaphoreReset
274 : *
275 : * Reset a previously-initialized PGSemaphore to have count 0
276 : */
277 : void
278 365 : PGSemaphoreReset(PGSemaphore sema)
279 : {
280 : /*
281 : * There's no direct API for this in POSIX, so we have to ratchet the
282 : * semaphore down to 0 with repeated trywait's.
283 : */
284 : for (;;)
285 : {
286 365 : if (sem_trywait(PG_SEM_REF(sema)) < 0)
287 : {
288 342 : if (errno == EAGAIN || errno == EDEADLK)
289 : break; /* got it down to 0 */
290 0 : if (errno == EINTR)
291 0 : continue; /* can this happen? */
292 0 : elog(FATAL, "sem_trywait failed: %m");
293 : }
294 23 : }
295 342 : }
296 :
297 : /*
298 : * PGSemaphoreLock
299 : *
300 : * Lock a semaphore (decrement count), blocking if count would be < 0
301 : */
302 : void
303 896 : PGSemaphoreLock(PGSemaphore sema)
304 : {
305 : int errStatus;
306 :
307 : /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */
308 : do
309 : {
310 896 : errStatus = sem_wait(PG_SEM_REF(sema));
311 896 : } while (errStatus < 0 && errno == EINTR);
312 :
313 896 : if (errStatus < 0)
314 0 : elog(FATAL, "sem_wait failed: %m");
315 896 : }
316 :
317 : /*
318 : * PGSemaphoreUnlock
319 : *
320 : * Unlock a semaphore (increment count)
321 : */
322 : void
323 896 : PGSemaphoreUnlock(PGSemaphore sema)
324 : {
325 : int errStatus;
326 :
327 : /*
328 : * Note: if errStatus is -1 and errno == EINTR then it means we returned
329 : * from the operation prematurely because we were sent a signal. So we
330 : * try and unlock the semaphore again. Not clear this can really happen,
331 : * but might as well cope.
332 : */
333 : do
334 : {
335 896 : errStatus = sem_post(PG_SEM_REF(sema));
336 896 : } while (errStatus < 0 && errno == EINTR);
337 :
338 896 : if (errStatus < 0)
339 0 : elog(FATAL, "sem_post failed: %m");
340 896 : }
341 :
342 : /*
343 : * PGSemaphoreTryLock
344 : *
345 : * Lock a semaphore only if able to do so without blocking
346 : */
347 : bool
348 0 : PGSemaphoreTryLock(PGSemaphore sema)
349 : {
350 : int errStatus;
351 :
352 : /*
353 : * Note: if errStatus is -1 and errno == EINTR then it means we returned
354 : * from the operation prematurely because we were sent a signal. So we
355 : * try and lock the semaphore again.
356 : */
357 : do
358 : {
359 0 : errStatus = sem_trywait(PG_SEM_REF(sema));
360 0 : } while (errStatus < 0 && errno == EINTR);
361 :
362 0 : if (errStatus < 0)
363 : {
364 0 : if (errno == EAGAIN || errno == EDEADLK)
365 0 : return false; /* failed to lock it */
366 : /* Otherwise we got trouble */
367 0 : elog(FATAL, "sem_trywait failed: %m");
368 : }
369 :
370 0 : return true;
371 : }
|