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