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-2023, 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 : 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 *
86 : PosixSemaphoreCreate(void)
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
135 138040 : PosixSemaphoreCreate(sem_t *sem)
136 : {
137 138040 : if (sem_init(sem, 1, 1) < 0)
138 0 : elog(FATAL, "sem_init failed: %m");
139 138040 : }
140 : #endif /* USE_NAMED_POSIX_SEMAPHORES */
141 :
142 :
143 : /*
144 : * PosixSemaphoreKill - removes a semaphore
145 : */
146 : static void
147 137338 : PosixSemaphoreKill(sem_t *sem)
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 137338 : if (sem_destroy(sem) < 0)
156 0 : elog(LOG, "sem_destroy failed: %m");
157 : #endif
158 137338 : }
159 :
160 :
161 : /*
162 : * Report amount of shared memory needed for semaphores
163 : */
164 : Size
165 4496 : 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 4496 : 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 1562 : 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 1562 : if (stat(DataDir, &statbuf) < 0)
207 0 : ereport(FATAL,
208 : (errcode_for_file_access(),
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 : */
223 1562 : sharedSemas = (PGSemaphore)
224 1562 : ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
225 : #endif
226 :
227 1562 : numSems = 0;
228 1562 : maxSems = maxSemas;
229 1562 : nextSemKey = statbuf.st_ino;
230 :
231 1562 : on_shmem_exit(ReleaseSemaphores, 0);
232 1562 : }
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
240 1556 : ReleaseSemaphores(int status, Datum arg)
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 138894 : for (i = 0; i < numSems; i++)
252 137338 : PosixSemaphoreKill(PG_SEM_REF(sharedSemas + i));
253 : #endif
254 1556 : }
255 :
256 : /*
257 : * PGSemaphoreCreate
258 : *
259 : * Allocate a PGSemaphore structure with initial count 1
260 : */
261 : PGSemaphore
262 138040 : PGSemaphoreCreate(void)
263 : {
264 : PGSemaphore sema;
265 : sem_t *newsem;
266 :
267 : /* Can't do this in a backend, because static state is postmaster's */
268 : Assert(!IsUnderPostmaster);
269 :
270 138040 : if (numSems >= maxSems)
271 0 : 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 138040 : sema = &sharedSemas[numSems];
280 138040 : newsem = PG_SEM_REF(sema);
281 138040 : PosixSemaphoreCreate(newsem);
282 : #endif
283 :
284 138040 : numSems++;
285 :
286 138040 : return sema;
287 : }
288 :
289 : /*
290 : * PGSemaphoreReset
291 : *
292 : * Reset a previously-initialized PGSemaphore to have count 0
293 : */
294 : void
295 44846 : PGSemaphoreReset(PGSemaphore sema)
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 44846 : if (sem_trywait(PG_SEM_REF(sema)) < 0)
304 : {
305 27794 : if (errno == EAGAIN || errno == EDEADLK)
306 : break; /* got it down to 0 */
307 0 : if (errno == EINTR)
308 0 : continue; /* can this happen? */
309 0 : elog(FATAL, "sem_trywait failed: %m");
310 : }
311 : }
312 27794 : }
313 :
314 : /*
315 : * PGSemaphoreLock
316 : *
317 : * Lock a semaphore (decrement count), blocking if count would be < 0
318 : */
319 : void
320 31236 : PGSemaphoreLock(PGSemaphore sema)
321 : {
322 : int errStatus;
323 :
324 : /* See notes in sysv_sema.c's implementation of PGSemaphoreLock. */
325 : do
326 : {
327 31236 : errStatus = sem_wait(PG_SEM_REF(sema));
328 31236 : } while (errStatus < 0 && errno == EINTR);
329 :
330 31236 : if (errStatus < 0)
331 0 : elog(FATAL, "sem_wait failed: %m");
332 31236 : }
333 :
334 : /*
335 : * PGSemaphoreUnlock
336 : *
337 : * Unlock a semaphore (increment count)
338 : */
339 : void
340 31236 : PGSemaphoreUnlock(PGSemaphore sema)
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 31236 : errStatus = sem_post(PG_SEM_REF(sema));
353 31236 : } while (errStatus < 0 && errno == EINTR);
354 :
355 31236 : if (errStatus < 0)
356 0 : elog(FATAL, "sem_post failed: %m");
357 31236 : }
358 :
359 : /*
360 : * PGSemaphoreTryLock
361 : *
362 : * Lock a semaphore only if able to do so without blocking
363 : */
364 : bool
365 0 : PGSemaphoreTryLock(PGSemaphore sema)
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 0 : errStatus = sem_trywait(PG_SEM_REF(sema));
377 0 : } while (errStatus < 0 && errno == EINTR);
378 :
379 0 : if (errStatus < 0)
380 : {
381 0 : if (errno == EAGAIN || errno == EDEADLK)
382 0 : return false; /* failed to lock it */
383 : /* Otherwise we got trouble */
384 0 : elog(FATAL, "sem_trywait failed: %m");
385 : }
386 :
387 0 : return true;
388 : }
|