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