YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/build/debugcov-clang-dynamic-arm64-ninja/postgres_build/src/backend/port/pg_sema.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * sysv_sema.c
4
 *    Implement PGSemaphores using SysV semaphore facilities
5
 *
6
 *
7
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8
 * Portions Copyright (c) 1994, Regents of the University of California
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/port/sysv_sema.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include <signal.h>
18
#include <unistd.h>
19
#include <sys/file.h>
20
#ifdef HAVE_SYS_IPC_H
21
#include <sys/ipc.h>
22
#endif
23
#ifdef HAVE_SYS_SEM_H
24
#include <sys/sem.h>
25
#endif
26
27
#include "miscadmin.h"
28
#include "storage/ipc.h"
29
#include "storage/pg_sema.h"
30
#include "storage/shmem.h"
31
32
33
typedef struct PGSemaphoreData
34
{
35
  int     semId;      /* semaphore set identifier */
36
  int     semNum;     /* semaphore number within set */
37
} PGSemaphoreData;
38
39
#ifndef HAVE_UNION_SEMUN
40
union semun
41
{
42
  int     val;
43
  struct semid_ds *buf;
44
  unsigned short *array;
45
};
46
#endif
47
48
typedef key_t IpcSemaphoreKey;  /* semaphore key passed to semget(2) */
49
typedef int IpcSemaphoreId;   /* semaphore ID returned by semget(2) */
50
51
/*
52
 * SEMAS_PER_SET is the number of useful semaphores in each semaphore set
53
 * we allocate.  It must be *less than* your kernel's SEMMSL (max semaphores
54
 * per set) parameter, which is often around 25.  (Less than, because we
55
 * allocate one extra sema in each set for identification purposes.)
56
 */
57
1.22M
#define SEMAS_PER_SET 16
58
59
72.4k
#define IPCProtection (0600)  /* access/modify by user only */
60
61
72.4k
#define PGSemaMagic   537    /* must be less than SEMVMX */
62
63
64
static PGSemaphore sharedSemas; /* array of PGSemaphoreData in shared memory */
65
static int  numSharedSemas;   /* number of PGSemaphoreDatas used so far */
66
static int  maxSharedSemas;   /* allocated size of PGSemaphoreData array */
67
static IpcSemaphoreId *mySemaSets;  /* IDs of sema sets acquired so far */
68
static int  numSemaSets;    /* number of sema sets acquired so far */
69
static int  maxSemaSets;    /* allocated size of mySemaSets array */
70
static IpcSemaphoreKey nextSemaKey; /* next key to try using */
71
static int  nextSemaNumber;   /* next free sem num in last sema set */
72
73
74
static IpcSemaphoreId InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey,
75
               int numSems);
76
static void IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum,
77
             int value);
78
static void IpcSemaphoreKill(IpcSemaphoreId semId);
79
static int  IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum);
80
static pid_t IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum);
81
static IpcSemaphoreId IpcSemaphoreCreate(int numSems);
82
static void ReleaseSemaphores(int status, Datum arg);
83
84
85
/*
86
 * InternalIpcSemaphoreCreate
87
 *
88
 * Attempt to create a new semaphore set with the specified key.
89
 * Will fail (return -1) if such a set already exists.
90
 *
91
 * If we fail with a failure code other than collision-with-existing-set,
92
 * print out an error and abort.  Other types of errors suggest nonrecoverable
93
 * problems.
94
 */
95
static IpcSemaphoreId
96
InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
97
72.4k
{
98
72.4k
  int     semId;
99
100
72.4k
  semId = semget(semKey, numSems, IPC_CREAT | IPC_EXCL | IPCProtection);
101
102
72.4k
  if (semId < 0)
103
199
  {
104
199
    int     saved_errno = errno;
105
106
    /*
107
     * Fail quietly if error indicates a collision with existing set. One
108
     * would expect EEXIST, given that we said IPC_EXCL, but perhaps we
109
     * could get a permission violation instead?  Also, EIDRM might occur
110
     * if an old set is slated for destruction but not gone yet.
111
     */
112
199
    if (saved_errno == EEXIST || saved_errno == EACCES
113
199
#ifdef EIDRM
114
0
      || saved_errno == EIDRM
115
199
#endif
116
199
      )
117
199
      return -1;
118
119
    /*
120
     * Else complain and abort
121
     */
122
0
    ereport(FATAL,
123
0
        (errmsg("could not create semaphores: %m"),
124
0
         errdetail("Failed system call was semget(%lu, %d, 0%o).",
125
0
               (unsigned long) semKey, numSems,
126
0
               IPC_CREAT | IPC_EXCL | IPCProtection),
127
0
         (saved_errno == ENOSPC) ?
128
0
         errhint("This error does *not* mean that you have run out of disk space.  "
129
0
             "It occurs when either the system limit for the maximum number of "
130
0
             "semaphore sets (SEMMNI), or the system wide maximum number of "
131
0
             "semaphores (SEMMNS), would be exceeded.  You need to raise the "
132
0
             "respective kernel parameter.  Alternatively, reduce PostgreSQL's "
133
0
             "consumption of semaphores by reducing its max_connections parameter.\n"
134
0
             "The PostgreSQL documentation contains more information about "
135
0
             "configuring your system for PostgreSQL.") : 0));
136
0
  }
137
138
72.2k
  return semId;
139
72.4k
}
140
141
/*
142
 * Initialize a semaphore to the specified value.
143
 */
144
static void
145
IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value)
146
1.21M
{
147
1.21M
  union semun semun;
148
149
1.21M
  semun.val = value;
150
1.21M
  if (semctl(semId, semNum, SETVAL, semun) < 0)
151
0
  {
152
0
    int     saved_errno = errno;
153
154
0
    ereport(FATAL,
155
0
        (errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m",
156
0
                 semId, semNum, value),
157
0
         (saved_errno == ERANGE) ?
158
0
         errhint("You possibly need to raise your kernel's SEMVMX value to be at least "
159
0
             "%d.  Look into the PostgreSQL documentation for details.",
160
0
             value) : 0));
161
0
  }
162
1.21M
}
163
164
/*
165
 * IpcSemaphoreKill(semId)  - removes a semaphore set
166
 */
167
static void
168
IpcSemaphoreKill(IpcSemaphoreId semId)
169
72.2k
{
170
72.2k
  union semun semun;
171
172
72.2k
  semun.val = 0;        /* unused, but keep compiler quiet */
173
174
72.2k
  if (semctl(semId, 0, IPC_RMID, semun) < 0)
175
0
    elog(LOG, "semctl(%d, 0, IPC_RMID, ...) failed: %m", semId);
176
72.2k
}
177
178
/* Get the current value (semval) of the semaphore */
179
static int
180
IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum)
181
199
{
182
199
  union semun dummy;      /* for Solaris */
183
184
199
  dummy.val = 0;        /* unused */
185
186
199
  return semctl(semId, semNum, GETVAL, dummy);
187
199
}
188
189
/* Get the PID of the last process to do semop() on the semaphore */
190
static pid_t
191
IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum)
192
199
{
193
199
  union semun dummy;      /* for Solaris */
194
195
199
  dummy.val = 0;        /* unused */
196
197
199
  return semctl(semId, semNum, GETPID, dummy);
198
199
}
199
200
201
/*
202
 * Create a semaphore set with the given number of useful semaphores
203
 * (an additional sema is actually allocated to serve as identifier).
204
 * Dead Postgres sema sets are recycled if found, but we do not fail
205
 * upon collision with non-Postgres sema sets.
206
 *
207
 * The idea here is to detect and re-use keys that may have been assigned
208
 * by a crashed postmaster or backend.
209
 */
210
static IpcSemaphoreId
211
IpcSemaphoreCreate(int numSems)
212
72.2k
{
213
72.2k
  IpcSemaphoreId semId;
214
72.2k
  union semun semun;
215
72.2k
  PGSemaphoreData mysema;
216
217
  /* Loop till we find a free IPC key */
218
199
  for (nextSemaKey++;; nextSemaKey++)
219
72.4k
  {
220
72.4k
    pid_t   creatorPID;
221
222
    /* Try to create new semaphore set */
223
72.4k
    semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
224
72.4k
    if (semId >= 0)
225
72.2k
      break;       /* successful create */
226
227
    /* See if it looks to be leftover from a dead Postgres process */
228
199
    semId = semget(nextSemaKey, numSems + 1, 0);
229
199
    if (semId < 0)
230
0
      continue;     /* failed: must be some other app's */
231
199
    if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic)
232
0
      continue;     /* sema belongs to a non-Postgres app */
233
234
    /*
235
     * If the creator PID is my own PID or does not belong to any extant
236
     * process, it's safe to zap it.
237
     */
238
199
    creatorPID = IpcSemaphoreGetLastPID(semId, numSems);
239
199
    if (creatorPID <= 0)
240
0
      continue;     /* oops, GETPID failed */
241
199
    if (creatorPID != getpid())
242
199
    {
243
199
      if (kill(creatorPID, 0) == 0 || errno != ESRCH)
244
199
        continue;   /* sema belongs to a live process */
245
0
    }
246
247
    /*
248
     * The sema set appears to be from a dead Postgres process, or from a
249
     * previous cycle of life in this same process.  Zap it, if possible.
250
     * This probably shouldn't fail, but if it does, assume the sema set
251
     * belongs to someone else after all, and continue quietly.
252
     */
253
0
    semun.val = 0;      /* unused, but keep compiler quiet */
254
0
    if (semctl(semId, 0, IPC_RMID, semun) < 0)
255
0
      continue;
256
257
    /*
258
     * Now try again to create the sema set.
259
     */
260
0
    semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
261
0
    if (semId >= 0)
262
0
      break;       /* successful create */
263
264
    /*
265
     * Can only get here if some other process managed to create the same
266
     * sema key before we did.  Let him have that one, loop around to try
267
     * next key.
268
     */
269
0
  }
270
271
  /*
272
   * OK, we created a new sema set.  Mark it as created by this process. We
273
   * do this by setting the spare semaphore to PGSemaMagic-1 and then
274
   * incrementing it with semop().  That leaves it with value PGSemaMagic
275
   * and sempid referencing this process.
276
   */
277
72.2k
  IpcSemaphoreInitialize(semId, numSems, PGSemaMagic - 1);
278
72.2k
  mysema.semId = semId;
279
72.2k
  mysema.semNum = numSems;
280
72.2k
  PGSemaphoreUnlock(&mysema);
281
282
72.2k
  return semId;
283
72.2k
}
284
285
286
/*
287
 * Report amount of shared memory needed for semaphores
288
 */
289
Size
290
PGSemaphoreShmemSize(int maxSemas)
291
7.22k
{
292
7.22k
  return mul_size(maxSemas, sizeof(PGSemaphoreData));
293
7.22k
}
294
295
/*
296
 * PGReserveSemaphores --- initialize semaphore support
297
 *
298
 * This is called during postmaster start or shared memory reinitialization.
299
 * It should do whatever is needed to be able to support up to maxSemas
300
 * subsequent PGSemaphoreCreate calls.  Also, if any system resources
301
 * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
302
 * callback to release them.
303
 *
304
 * The port number is passed for possible use as a key (for SysV, we use
305
 * it to generate the starting semaphore key).  In a standalone backend,
306
 * zero will be passed.
307
 *
308
 * In the SysV implementation, we acquire semaphore sets on-demand; the
309
 * maxSemas parameter is just used to size the arrays.  There is an array
310
 * of PGSemaphoreData structs in shared memory, and a postmaster-local array
311
 * with one entry per SysV semaphore set, which we use for releasing the
312
 * semaphore sets when done.  (This design ensures that postmaster shutdown
313
 * doesn't rely on the contents of shared memory, which a failed backend might
314
 * have clobbered.)
315
 */
316
void
317
PGReserveSemaphores(int maxSemas, int port)
318
3.61k
{
319
  /*
320
   * We must use ShmemAllocUnlocked(), since the spinlock protecting
321
   * ShmemAlloc() won't be ready yet.  (This ordering is necessary when we
322
   * are emulating spinlocks with semaphores.)
323
   */
324
3.61k
  sharedSemas = (PGSemaphore)
325
3.61k
    ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
326
3.61k
  numSharedSemas = 0;
327
3.61k
  maxSharedSemas = maxSemas;
328
329
3.61k
  maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET;
330
3.61k
  mySemaSets = (IpcSemaphoreId *)
331
3.61k
    malloc(maxSemaSets * sizeof(IpcSemaphoreId));
332
3.61k
  if (mySemaSets == NULL)
333
0
    elog(PANIC, "out of memory");
334
3.61k
  numSemaSets = 0;
335
3.61k
  nextSemaKey = port * 1000;
336
3.61k
  nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */
337
338
3.61k
  on_shmem_exit(ReleaseSemaphores, 0);
339
3.61k
}
340
341
/*
342
 * Release semaphores at shutdown or shmem reinitialization
343
 *
344
 * (called as an on_shmem_exit callback, hence funny argument list)
345
 */
346
static void
347
ReleaseSemaphores(int status, Datum arg)
348
3.61k
{
349
3.61k
  int     i;
350
351
75.8k
  for (i = 0; i < numSemaSets; i++)
352
72.2k
    IpcSemaphoreKill(mySemaSets[i]);
353
3.61k
  free(mySemaSets);
354
3.61k
}
355
356
/*
357
 * PGSemaphoreCreate
358
 *
359
 * Allocate a PGSemaphore structure with initial count 1
360
 */
361
PGSemaphore
362
PGSemaphoreCreate(void)
363
1.14M
{
364
1.14M
  PGSemaphore sema;
365
366
  /* Can't do this in a backend, because static state is postmaster's */
367
1.14M
  Assert(!IsUnderPostmaster);
368
369
1.14M
  if (nextSemaNumber >= SEMAS_PER_SET)
370
72.2k
  {
371
    /* Time to allocate another semaphore set */
372
72.2k
    if (numSemaSets >= maxSemaSets)
373
0
      elog(PANIC, "too many semaphores created");
374
72.2k
    mySemaSets[numSemaSets] = IpcSemaphoreCreate(SEMAS_PER_SET);
375
72.2k
    numSemaSets++;
376
72.2k
    nextSemaNumber = 0;
377
72.2k
  }
378
  /* Use the next shared PGSemaphoreData */
379
1.14M
  if (numSharedSemas >= maxSharedSemas)
380
0
    elog(PANIC, "too many semaphores created");
381
1.14M
  sema = &sharedSemas[numSharedSemas++];
382
  /* Assign the next free semaphore in the current set */
383
1.14M
  sema->semId = mySemaSets[numSemaSets - 1];
384
1.14M
  sema->semNum = nextSemaNumber++;
385
  /* Initialize it to count 1 */
386
1.14M
  IpcSemaphoreInitialize(sema->semId, sema->semNum, 1);
387
388
1.14M
  return sema;
389
1.14M
}
390
391
/*
392
 * PGSemaphoreReset
393
 *
394
 * Reset a previously-initialized PGSemaphore to have count 0
395
 */
396
void
397
PGSemaphoreReset(PGSemaphore sema)
398
5.26k
{
399
5.26k
  IpcSemaphoreInitialize(sema->semId, sema->semNum, 0);
400
5.26k
}
401
402
/*
403
 * PGSemaphoreLock
404
 *
405
 * Lock a semaphore (decrement count), blocking if count would be < 0
406
 */
407
void
408
PGSemaphoreLock(PGSemaphore sema)
409
354
{
410
354
  int     errStatus;
411
354
  struct sembuf sops;
412
413
354
  sops.sem_op = -1;     /* decrement */
414
354
  sops.sem_flg = 0;
415
354
  sops.sem_num = sema->semNum;
416
417
  /*
418
   * Note: if errStatus is -1 and errno == EINTR then it means we returned
419
   * from the operation prematurely because we were sent a signal.  So we
420
   * try and lock the semaphore again.
421
   *
422
   * We used to check interrupts here, but that required servicing
423
   * interrupts directly from signal handlers. Which is hard to do safely
424
   * and portably.
425
   */
426
354
  do
427
354
  {
428
354
    errStatus = semop(sema->semId, &sops, 1);
429
354
  } while (errStatus < 0 && errno == EINTR);
430
431
354
  if (errStatus < 0)
432
1
    elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
433
354
}
434
435
/*
436
 * PGSemaphoreUnlock
437
 *
438
 * Unlock a semaphore (increment count)
439
 */
440
void
441
PGSemaphoreUnlock(PGSemaphore sema)
442
72.6k
{
443
72.6k
  int     errStatus;
444
72.6k
  struct sembuf sops;
445
446
72.6k
  sops.sem_op = 1;      /* increment */
447
72.6k
  sops.sem_flg = 0;
448
72.6k
  sops.sem_num = sema->semNum;
449
450
  /*
451
   * Note: if errStatus is -1 and errno == EINTR then it means we returned
452
   * from the operation prematurely because we were sent a signal.  So we
453
   * try and unlock the semaphore again. Not clear this can really happen,
454
   * but might as well cope.
455
   */
456
72.6k
  do
457
72.6k
  {
458
72.6k
    errStatus = semop(sema->semId, &sops, 1);
459
72.6k
  } while (errStatus < 0 && errno == EINTR);
460
461
72.6k
  if (errStatus < 0)
462
1
    elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
463
72.6k
}
464
465
/*
466
 * PGSemaphoreTryLock
467
 *
468
 * Lock a semaphore only if able to do so without blocking
469
 */
470
bool
471
PGSemaphoreTryLock(PGSemaphore sema)
472
0
{
473
0
  int     errStatus;
474
0
  struct sembuf sops;
475
476
0
  sops.sem_op = -1;     /* decrement */
477
0
  sops.sem_flg = IPC_NOWAIT;  /* but don't block */
478
0
  sops.sem_num = sema->semNum;
479
480
  /*
481
   * Note: if errStatus is -1 and errno == EINTR then it means we returned
482
   * from the operation prematurely because we were sent a signal.  So we
483
   * try and lock the semaphore again.
484
   */
485
0
  do
486
0
  {
487
0
    errStatus = semop(sema->semId, &sops, 1);
488
0
  } while (errStatus < 0 && errno == EINTR);
489
490
0
  if (errStatus < 0)
491
0
  {
492
    /* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */
493
0
#ifdef EAGAIN
494
0
    if (errno == EAGAIN)
495
0
      return false;   /* failed to lock it */
496
0
#endif
497
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
498
    if (errno == EWOULDBLOCK)
499
      return false;   /* failed to lock it */
500
#endif
501
    /* Otherwise we got trouble */
502
0
    elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
503
0
  }
504
505
0
  return true;
506
0
}