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-2024, 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;
50 
51 /* typedef PGSemaphore is equivalent to pointer to sem_t */
52 typedef 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
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 static int nextSemKey; /* next name to try */
69 
70 
71 static 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  */
85 static 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  */
134 static 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  */
146 static 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  */
164 Size
165 PGSemaphoreShmemSize(int maxSemas)
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  */
195 void
196 PGReserveSemaphores(int maxSemas)
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)
207  ereport(FATAL,
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  */
238 static 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  */
293 void
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  */
318 void
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  */
338 void
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  */
363 bool
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 }
#define Assert(condition)
Definition: c.h:812
size_t Size
Definition: c.h:559
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
#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:72
void * arg
#define PG_CACHE_LINE_SIZE
struct PGSemaphoreData * PGSemaphore
Definition: pg_sema.h:34
#define snprintf
Definition: port.h:238
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:64
Size mul_size(Size s1, Size s2)
Definition: shmem.c:505
void * ShmemAllocUnlocked(Size size)
Definition: shmem.c:233
SemTPadded sem_padded
Definition: posix_sema.c:54
_ino_t st_ino
Definition: win32_port.h:267
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:284
#define EINTR
Definition: win32_port.h:374
#define EAGAIN
Definition: win32_port.h:372