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. (This ordering is necessary when we
221  * are emulating spinlocks with semaphores.)
222  */
225 #endif
226 
227  numSems = 0;
228  maxSems = maxSemas;
229  nextSemKey = statbuf.st_ino;
230 
232 }
233 
234 /*
235  * Release semaphores at shutdown or shmem reinitialization
236  *
237  * (called as an on_shmem_exit callback, hence funny argument list)
238  */
239 static void
241 {
242  int i;
243 
244 #ifdef USE_NAMED_POSIX_SEMAPHORES
245  for (i = 0; i < numSems; i++)
246  PosixSemaphoreKill(mySemPointers[i]);
247  free(mySemPointers);
248 #endif
249 
250 #ifdef USE_UNNAMED_POSIX_SEMAPHORES
251  for (i = 0; i < numSems; i++)
253 #endif
254 }
255 
256 /*
257  * PGSemaphoreCreate
258  *
259  * Allocate a PGSemaphore structure with initial count 1
260  */
263 {
264  PGSemaphore sema;
265  sem_t *newsem;
266 
267  /* Can't do this in a backend, because static state is postmaster's */
269 
270  if (numSems >= maxSems)
271  elog(PANIC, "too many semaphores created");
272 
273 #ifdef USE_NAMED_POSIX_SEMAPHORES
274  newsem = PosixSemaphoreCreate();
275  /* Remember new sema for ReleaseSemaphores */
276  mySemPointers[numSems] = newsem;
277  sema = (PGSemaphore) newsem;
278 #else
279  sema = &sharedSemas[numSems];
280  newsem = PG_SEM_REF(sema);
281  PosixSemaphoreCreate(newsem);
282 #endif
283 
284  numSems++;
285 
286  return sema;
287 }
288 
289 /*
290  * PGSemaphoreReset
291  *
292  * Reset a previously-initialized PGSemaphore to have count 0
293  */
294 void
296 {
297  /*
298  * There's no direct API for this in POSIX, so we have to ratchet the
299  * semaphore down to 0 with repeated trywait's.
300  */
301  for (;;)
302  {
303  if (sem_trywait(PG_SEM_REF(sema)) < 0)
304  {
305  if (errno == EAGAIN || errno == EDEADLK)
306  break; /* got it down to 0 */
307  if (errno == EINTR)
308  continue; /* can this happen? */
309  elog(FATAL, "sem_trywait failed: %m");
310  }
311  }
312 }
313 
314 /*
315  * PGSemaphoreLock
316  *
317  * Lock a semaphore (decrement count), blocking if count would be < 0
318  */
319 void
321 {
322  int errStatus;
323 
324  /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */
325  do
326  {
327  errStatus = sem_wait(PG_SEM_REF(sema));
328  } while (errStatus < 0 && errno == EINTR);
329 
330  if (errStatus < 0)
331  elog(FATAL, "sem_wait failed: %m");
332 }
333 
334 /*
335  * PGSemaphoreUnlock
336  *
337  * Unlock a semaphore (increment count)
338  */
339 void
341 {
342  int errStatus;
343 
344  /*
345  * Note: if errStatus is -1 and errno == EINTR then it means we returned
346  * from the operation prematurely because we were sent a signal. So we
347  * try and unlock the semaphore again. Not clear this can really happen,
348  * but might as well cope.
349  */
350  do
351  {
352  errStatus = sem_post(PG_SEM_REF(sema));
353  } while (errStatus < 0 && errno == EINTR);
354 
355  if (errStatus < 0)
356  elog(FATAL, "sem_post failed: %m");
357 }
358 
359 /*
360  * PGSemaphoreTryLock
361  *
362  * Lock a semaphore only if able to do so without blocking
363  */
364 bool
366 {
367  int errStatus;
368 
369  /*
370  * Note: if errStatus is -1 and errno == EINTR then it means we returned
371  * from the operation prematurely because we were sent a signal. So we
372  * try and lock the semaphore again.
373  */
374  do
375  {
376  errStatus = sem_trywait(PG_SEM_REF(sema));
377  } while (errStatus < 0 && errno == EINTR);
378 
379  if (errStatus < 0)
380  {
381  if (errno == EAGAIN || errno == EDEADLK)
382  return false; /* failed to lock it */
383  /* Otherwise we got trouble */
384  elog(FATAL, "sem_trywait failed: %m");
385  }
386 
387  return true;
388 }
size_t Size
Definition: c.h:592
int errcode_for_file_access(void)
Definition: elog.c:882
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define LOG
Definition: elog.h:31
#define FATAL
Definition: elog.h:41
#define PANIC
Definition: elog.h:42
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
bool IsUnderPostmaster
Definition: globals.c:117
char * DataDir
Definition: globals.c:68
#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:73
Assert(fmt[strlen(fmt) - 1] !='\n')
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:340
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:295
void PGSemaphoreLock(PGSemaphore sema)
Definition: posix_sema.c:320
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:365
static PGSemaphore sharedSemas
Definition: posix_sema.c:64
PGSemaphore PGSemaphoreCreate(void)
Definition: posix_sema.c:262
static void ReleaseSemaphores(int status, Datum arg)
Definition: posix_sema.c:240
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:510
void * ShmemAllocUnlocked(Size size)
Definition: shmem.c:238
SemTPadded sem_padded
Definition: posix_sema.c:54
_ino_t st_ino
Definition: win32_port.h:267
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