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