1 /* -*-pgsql-c-*- */
2 /*
3 * $Header$
4 *
5 * pgpool: a language independent connection pool server for PostgreSQL
6 * written by Tatsuo Ishii
7 *
8 * Portions Copyright (c) 2003-2014, PgPool Global Development Group
9 * Portions Copyright (c) 2003-2004, PostgreSQL Global Development Group
10 *
11 * Permission to use, copy, modify, and distribute this software and
12 * its documentation for any purpose and without fee is hereby
13 * granted, provided that the above copyright notice appear in all
14 * copies and that both that copyright notice and this permission
15 * notice appear in supporting documentation, and that the name of the
16 * author not be used in advertising or publicity pertaining to
17 * distribution of the software without specific, written prior
18 * permission. The author makes no representations about the
19 * suitability of this software for any purpose. It is provided "as
20 * is" without express or implied warranty.
21 *
22 */
23 #include "pool.h"
24
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/sem.h>
28 #include "utils/elog.h"
29 #include "utils/pool_ipc.h"
30
31
32 #ifndef HAVE_UNION_SEMUN
33 union semun
34 {
35 int val;
36 struct semid_ds *buf;
37 unsigned short *array;
38 };
39 #endif
40
41
42 static int semId;
43
44
45 /*
46 * Removes a semaphore set.
47 */
48 static void
IpcSemaphoreKill(int status,Datum semId)49 IpcSemaphoreKill(int status, Datum semId)
50 {
51 union semun semun;
52 struct semid_ds seminfo;
53
54 /*
55 * Is a previously-existing sema segment still existing and in use?
56 */
57 semun.buf = &seminfo;
58 if (semctl(semId, 0, IPC_STAT, semun) < 0
59 && (errno == EINVAL || errno == EACCES
60 #ifdef EIDRM
61 || errno == EIDRM
62 #endif
63 ))
64 return;
65
66 semun.val = 0; /* unused, but keep compiler quiet */
67
68 if (semctl(semId, 0, IPC_RMID) < 0)
69 ereport(LOG,
70 (errmsg("removing semaphore set"),
71 errdetail("semctl(%lu, 0, IPC_RMID, ...) failed: %s", semId, strerror(errno))));
72 }
73
74 /*
75 * Create a semaphore set and initialize.
76 */
77 void
pool_semaphore_create(int numSems)78 pool_semaphore_create(int numSems)
79 {
80 int i;
81
82 /* Try to create new semaphore set */
83 semId = semget(IPC_PRIVATE, numSems, IPC_CREAT | IPC_EXCL | IPCProtection);
84
85 if (semId < 0)
86 ereport(FATAL,
87 (errmsg("Unable to create semaphores:%d error:\"%s\"",numSems,strerror(errno))));
88
89 on_shmem_exit(IpcSemaphoreKill, semId);
90
91 /* Initialize it to count 1 */
92 for (i = 0; i < numSems; i++)
93 {
94 union semun semun;
95
96 semun.val = 1;
97 if (semctl(semId, i, SETVAL, semun) < 0)
98 ereport(FATAL,
99 (errmsg("Unable to create semaphores:%d error:\"%s\"",numSems,strerror(errno)),
100 errdetail("semctl(%d, %d, SETVAL, %d) failed",semId,i,semun.val)));
101 }
102 }
103
104 /*
105 * Lock a semaphore (decrement count), blocking if count would be < 0
106 */
107 void
pool_semaphore_lock(int semNum)108 pool_semaphore_lock(int semNum)
109 {
110 int errStatus;
111 struct sembuf sops;
112
113 sops.sem_op = -1; /* decrement */
114 sops.sem_flg = SEM_UNDO;
115 sops.sem_num = semNum;
116
117 /*
118 * Note: if errStatus is -1 and errno == EINTR then it means we returned
119 * from the operation prematurely because we were sent a signal. So we
120 * try and lock the semaphore again.
121 */
122 do
123 {
124 errStatus = semop(semId, &sops, 1);
125 } while (errStatus < 0 && errno == EINTR);
126
127 if (errStatus < 0)
128 ereport(WARNING,
129 (errmsg("failed to lock semaphore error:\"%s\"",strerror(errno))));
130 }
131
132 /*
133 * Lock a semaphore (decrement count), blocking if count would be < 0.
134 * Unlike pool_semaphore_lock, this returns if interrupted.
135 * Return values:
136 * 0: succeeded in acquiring lock.
137 * -1: error.
138 * -2: interrupted.
139 */
140 int
pool_semaphore_lock_allow_interrupt(int semNum)141 pool_semaphore_lock_allow_interrupt(int semNum)
142 {
143 int errStatus;
144 struct sembuf sops;
145
146 sops.sem_op = -1; /* decrement */
147 sops.sem_flg = SEM_UNDO;
148 sops.sem_num = semNum;
149
150 /*
151 * Note: if errStatus is -1 and errno == EINTR then it means we returned
152 * from the operation prematurely because we were sent a signal.
153 */
154 errStatus = semop(semId, &sops, 1);
155
156 if (errStatus < 0)
157 {
158 if (errno == EINTR)
159 {
160 ereport(DEBUG1,
161 (errmsg("interrupted while trying to lock semaphore")));
162 return -2;
163 }
164 else
165 {
166 ereport(WARNING,
167 (errmsg("failed to lock semaphore error:\"%s\"", strerror(errno))));
168 return -1;
169 }
170 }
171 return 0;
172 }
173
174 /*
175 * Unlock a semaphore (increment count)
176 */
177 void
pool_semaphore_unlock(int semNum)178 pool_semaphore_unlock(int semNum)
179 {
180 int errStatus;
181 struct sembuf sops;
182
183 sops.sem_op = 1; /* increment */
184 sops.sem_flg = SEM_UNDO;
185 sops.sem_num = semNum;
186
187 /*
188 * Note: if errStatus is -1 and errno == EINTR then it means we returned
189 * from the operation prematurely because we were sent a signal. So we
190 * try and unlock the semaphore again. Not clear this can really happen,
191 * but might as well cope.
192 */
193 do
194 {
195 errStatus = semop(semId, &sops, 1);
196 } while (errStatus < 0 && errno == EINTR);
197
198 if (errStatus < 0)
199 ereport(WARNING,
200 (errmsg("failed to unlock semaphore error:\"%s\"",strerror(errno))));
201 }
202