PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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-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
50
51/* typedef PGSemaphore is equivalent to pointer to sem_t */
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 {
95
96 snprintf(semname, sizeof(semname), "/pgsql-%d", semKey);
97
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 */
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 * Request shared memory needed for semaphores
163 */
164void
166{
167#ifdef USE_NAMED_POSIX_SEMAPHORES
168 /* No shared memory needed in this case */
169#else
170 /* Need a PGSemaphoreData per semaphore */
171 ShmemRequestStruct(.name = "Semaphores",
172 .size = mul_size(maxSemas, sizeof(PGSemaphoreData)),
173 .ptr = (void **) &sharedSemas,
174 );
175#endif
176}
177
178/*
179 * PGSemaphoreInit --- initialize semaphore support
180 *
181 * This is called during postmaster start or shared memory reinitialization.
182 * It should do whatever is needed to be able to support up to maxSemas
183 * subsequent PGSemaphoreCreate calls. Also, if any system resources
184 * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
185 * callback to release them.
186 *
187 * In the Posix implementation, we acquire semaphores on-demand; the
188 * maxSemas parameter is just used to size the arrays. For unnamed
189 * semaphores, there is an array of PGSemaphoreData structs in shared memory.
190 * For named semaphores, we keep a postmaster-local array of sem_t pointers,
191 * which we use for releasing the semaphores when done.
192 * (This design minimizes the dependency of postmaster shutdown on the
193 * contents of shared memory, which a failed backend might have clobbered.
194 * We can't do much about the possibility of sem_destroy() crashing, but
195 * we don't have to expose the counters to other processes.)
196 */
197void
199{
200 struct stat statbuf;
201
202 /*
203 * We use the data directory's inode number to seed the search for free
204 * semaphore keys. This minimizes the odds of collision with other
205 * postmasters, while maximizing the odds that we will detect and clean up
206 * semaphores left over from a crashed postmaster in our own directory.
207 */
208 if (stat(DataDir, &statbuf) < 0)
211 errmsg("could not stat data directory \"%s\": %m",
212 DataDir)));
213
214#ifdef USE_NAMED_POSIX_SEMAPHORES
215 mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *));
216 if (mySemPointers == NULL)
217 elog(PANIC, "out of memory");
218#endif
219
220 numSems = 0;
222 nextSemKey = statbuf.st_ino;
223
225}
226
227/*
228 * Release semaphores at shutdown or shmem reinitialization
229 *
230 * (called as an on_shmem_exit callback, hence funny argument list)
231 */
232static void
234{
235 int i;
236
237#ifdef USE_NAMED_POSIX_SEMAPHORES
238 for (i = 0; i < numSems; i++)
241#endif
242
243#ifdef USE_UNNAMED_POSIX_SEMAPHORES
244 for (i = 0; i < numSems; i++)
246#endif
247}
248
249/*
250 * PGSemaphoreCreate
251 *
252 * Allocate a PGSemaphore structure with initial count 1
253 */
256{
257 PGSemaphore sema;
258 sem_t *newsem;
259
260 /* Can't do this in a backend, because static state is postmaster's */
262
263 if (numSems >= maxSems)
264 elog(PANIC, "too many semaphores created");
265
266#ifdef USE_NAMED_POSIX_SEMAPHORES
268 /* Remember new sema for ReleaseSemaphores */
270 sema = (PGSemaphore) newsem;
271#else
272 sema = &sharedSemas[numSems];
273 newsem = PG_SEM_REF(sema);
275#endif
276
277 numSems++;
278
279 return sema;
280}
281
282/*
283 * PGSemaphoreReset
284 *
285 * Reset a previously-initialized PGSemaphore to have count 0
286 */
287void
289{
290 /*
291 * There's no direct API for this in POSIX, so we have to ratchet the
292 * semaphore down to 0 with repeated trywait's.
293 */
294 for (;;)
295 {
296 if (sem_trywait(PG_SEM_REF(sema)) < 0)
297 {
298 if (errno == EAGAIN || errno == EDEADLK)
299 break; /* got it down to 0 */
300 if (errno == EINTR)
301 continue; /* can this happen? */
302 elog(FATAL, "sem_trywait failed: %m");
303 }
304 }
305}
306
307/*
308 * PGSemaphoreLock
309 *
310 * Lock a semaphore (decrement count), blocking if count would be < 0
311 */
312void
314{
315 int errStatus;
316
317 /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */
318 do
319 {
321 } while (errStatus < 0 && errno == EINTR);
322
323 if (errStatus < 0)
324 elog(FATAL, "sem_wait failed: %m");
325}
326
327/*
328 * PGSemaphoreUnlock
329 *
330 * Unlock a semaphore (increment count)
331 */
332void
334{
335 int errStatus;
336
337 /*
338 * Note: if errStatus is -1 and errno == EINTR then it means we returned
339 * from the operation prematurely because we were sent a signal. So we
340 * try and unlock the semaphore again. Not clear this can really happen,
341 * but might as well cope.
342 */
343 do
344 {
346 } while (errStatus < 0 && errno == EINTR);
347
348 if (errStatus < 0)
349 elog(FATAL, "sem_post failed: %m");
350}
351
352/*
353 * PGSemaphoreTryLock
354 *
355 * Lock a semaphore only if able to do so without blocking
356 */
357bool
359{
360 int errStatus;
361
362 /*
363 * Note: if errStatus is -1 and errno == EINTR then it means we returned
364 * from the operation prematurely because we were sent a signal. So we
365 * try and lock the semaphore again.
366 */
367 do
368 {
370 } while (errStatus < 0 && errno == EINTR);
371
372 if (errStatus < 0)
373 {
374 if (errno == EAGAIN || errno == EDEADLK)
375 return false; /* failed to lock it */
376 /* Otherwise we got trouble */
377 elog(FATAL, "sem_trywait failed: %m");
378 }
379
380 return true;
381}
#define Assert(condition)
Definition c.h:943
Datum arg
Definition elog.c:1322
int errcode_for_file_access(void)
Definition elog.c:897
#define LOG
Definition elog.h:32
#define FATAL
Definition elog.h:42
#define PANIC
Definition elog.h:44
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool IsUnderPostmaster
Definition globals.c:122
char * DataDir
Definition globals.c:73
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition ipc.c:372
int i
Definition isn.c:77
static char * errmsg
#define PG_CACHE_LINE_SIZE
struct PGSemaphoreData * PGSemaphore
Definition pg_sema.h:34
#define snprintf
Definition port.h:260
void PGSemaphoreUnlock(PGSemaphore sema)
Definition posix_sema.c:333
static int maxSems
Definition posix_sema.c:67
static void PosixSemaphoreKill(sem_t *sem)
Definition posix_sema.c:147
struct PGSemaphoreData PGSemaphoreData
void PGSemaphoreShmemRequest(int maxSemas)
Definition posix_sema.c:165
void PGSemaphoreReset(PGSemaphore sema)
Definition posix_sema.c:288
void PGSemaphoreLock(PGSemaphore sema)
Definition posix_sema.c:313
void PGSemaphoreInit(int maxSemas)
Definition posix_sema.c:198
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:358
static PGSemaphore sharedSemas
Definition posix_sema.c:64
PGSemaphore PGSemaphoreCreate(void)
Definition posix_sema.c:255
static void ReleaseSemaphores(int status, Datum arg)
Definition posix_sema.c:233
static void PosixSemaphoreCreate(sem_t *sem)
Definition posix_sema.c:135
#define PG_SEM_REF(x)
Definition posix_sema.c:57
uint64_t Datum
Definition postgres.h:70
static int fb(int x)
Size mul_size(Size s1, Size s2)
Definition shmem.c:1063
#define ShmemRequestStruct(...)
Definition shmem.h:176
#define free(a)
#define malloc(a)
SemTPadded sem_padded
Definition posix_sema.c:54
static JsonSemAction sem
char pad[PG_CACHE_LINE_SIZE]
Definition posix_sema.c:48
sem_t pgsem
Definition posix_sema.c:47
const char * name
#define stat
Definition win32_port.h:74
#define EINTR
Definition win32_port.h:361
#define EAGAIN
Definition win32_port.h:359