PageRenderTime 65ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/backend/port/sysv_sema.c

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