blob: a84cbbb2b154c96cec199b77e558690031467650 [file] [log] [blame]
David Hendricks4b832f52011-01-20 14:28:34 -08001/*
2 * Copyright 2003 Sun Microsystems, Inc.
3 * Copyright 2010 Google, Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Developer's note: This was open sourced by Sun Microsystems, which got it
32 * via Cobalt Networks. It has been fairly extensively modified since then.
33 */
34
35#ifndef _GNU_SOURCE
36# define _GNU_SOURCE 1
37#endif
38#include <sys/types.h>
39#include <sys/ipc.h>
40#include <sys/sem.h>
41#include <sys/stat.h>
42#include <time.h>
43#include <errno.h>
44#include <sched.h>
45
46#include "csem.h"
47
48#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
49/* union semun is defined by including <sys/sem.h> */
50#else
51/* according to X/OPEN we have to define it ourselves */
52union semun {
53 int val; /* value for SETVAL */
54 struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
55 unsigned short int *array; /* array for GETALL, SETALL */
56 struct seminfo *__buf; /* buffer for IPC_INFO */
57};
58#endif
59
60/*
61 * On some platforms semctl(SETVAL) sets sem_otime, on other platforms it
62 * does not. Figure out what this platform does.
63 *
64 * Returns 0 if semctl(SETVAL) does not set sem_otime
65 * Returns 1 if semctl(SETVAL) does set sem_otime
66 * Returns -1 on error
67 */
68static int does_semctl_set_otime(void)
69{
70 int sem_id;
71 int ret;
72
73 /* create a test semaphore */
74 sem_id = semget(IPC_PRIVATE, 1, S_IRUSR|S_IWUSR);
75 if (sem_id < 0) {
76 return -1;
77 }
78
79 /* set the value */
80 if (csem_setval(sem_id, 1) < 0) {
81 csem_destroy(sem_id);
82 return -1;
83 }
84
85 /* read sem_otime */
86 ret = (csem_get_otime(sem_id) > 0) ? 1 : 0;
87
88 /* clean up */
89 csem_destroy(sem_id);
90
91 return ret;
92}
93
94int csem_create(key_t key, unsigned val)
95{
96 static int need_otime_hack = -1;
97 int sem_id;
98
99 /* see if we need to trigger a semop to set sem_otime */
100 if (need_otime_hack < 0) {
101 int ret = does_semctl_set_otime();
102 if (ret < 0) {
103 return -1;
104 }
105 need_otime_hack = !ret;
106 }
107
108 /* create it or fail */
109 sem_id = semget(key, 1, IPC_CREAT|IPC_EXCL | S_IRUSR|S_IWUSR);
110 if (sem_id < 0) {
111 return -1;
112 }
113
114 /* initalize the value */
115 if (need_otime_hack) {
116 val++;
117 }
118 if (csem_setval(sem_id, val) < 0) {
119 csem_destroy(sem_id);
120 return -1;
121 }
122
123 if (need_otime_hack) {
124 /* force sem_otime to change */
125 csem_down(sem_id);
126 }
127
128 return sem_id;
129}
130
131/* how many times to loop, waiting for sem_otime */
132#define MAX_OTIME_LOOPS 1000
133
134int csem_get(key_t key)
135{
136 int sem_id;
137 int i;
138
139 /* CSEM_PRIVATE needs to go through csem_create() to get an
140 * initial value */
141 if (key == CSEM_PRIVATE) {
142 errno = EINVAL;
143 return -1;
144 }
145
146 /* get the (assumed existing) semaphore */
147 sem_id = semget(key, 1, S_IRUSR|S_IWUSR);
148 if (sem_id < 0) {
149 return -1;
150 }
151
152 /* loop until sem_otime != 0, which means it has been initialized */
153 for (i = 0; i < MAX_OTIME_LOOPS; i++) {
154 time_t otime = csem_get_otime(sem_id);
155 if (otime < 0) {
156 /* error */
157 return -1;
158 }
159 if (otime > 0) {
160 /* success */
161 return sem_id;
162 }
163 /* retry */
164 sched_yield();
165 }
166
167 /* fell through - error */
168 return -1;
169}
170
171int csem_get_or_create(key_t key, unsigned val)
172{
173 int sem_id;
174
175 /* try to create the semaphore */
176 sem_id = csem_create(key, val);
177 if (sem_id >= 0 || errno != EEXIST) {
178 /* it either succeeded or got an error */
179 return sem_id;
180 }
181
182 /* it must exist already - get it */
183 sem_id = csem_get(key);
184 if (sem_id < 0) {
185 return -1;
186 }
187
188 return sem_id;
189}
190
191int csem_destroy(int sem_id)
192{
193 return semctl(sem_id, 0, IPC_RMID);
194}
195
196int csem_getval(int sem_id)
197{
198 return semctl(sem_id, 0, GETVAL);
199}
200
201int csem_setval(int sem_id, unsigned val)
202{
203 union semun arg;
204 arg.val = val;
205 if (semctl(sem_id, 0, SETVAL, arg) < 0) {
206 return -1;
207 }
208 return 0;
209}
210
211static int csem_up_undoflag(int sem_id, int undoflag)
212{
213 struct sembuf sops;
214 sops.sem_num = 0;
215 sops.sem_op = 1;
216 sops.sem_flg = undoflag;
217 return semop(sem_id, &sops, 1);
218}
219
220int csem_up(int sem_id)
221{
222 return csem_up_undoflag(sem_id, 0);
223}
224
225int csem_up_undo(int sem_id)
226{
227 return csem_up_undoflag(sem_id, SEM_UNDO);
228}
229
230static int csem_down_undoflag(int sem_id, int undoflag)
231{
232 struct sembuf sops;
233 sops.sem_num = 0;
234 sops.sem_op = -1;
235 sops.sem_flg = undoflag;
236 return semop(sem_id, &sops, 1);
237}
238
239int csem_down(int sem_id)
240{
241 return csem_down_undoflag(sem_id, 0);
242}
243
244int csem_down_undo(int sem_id)
245{
246 return csem_down_undoflag(sem_id, SEM_UNDO);
247}
248
249static int csem_down_timeout_undoflag(int sem_id,
250 struct timespec *timeout,
251 int undoflag)
252{
253 struct sembuf sops;
254 sops.sem_num = 0;
255 sops.sem_op = -1;
256 sops.sem_flg = undoflag;
257 return semtimedop(sem_id, &sops, 1, timeout);
258}
259
260int csem_down_timeout(int sem_id, struct timespec *timeout)
261{
262 return csem_down_timeout_undoflag(sem_id, timeout, 0);
263}
264
265int csem_down_timeout_undo(int sem_id, struct timespec *timeout)
266{
267 return csem_down_timeout_undoflag(sem_id, timeout, SEM_UNDO);
268}
269
270time_t csem_get_otime(int sem_id)
271{
272 union semun arg;
273 struct semid_ds ds;
274 arg.buf = &ds;
275 if (semctl(sem_id, 0, IPC_STAT, arg) < 0) {
276 return -1;
277 }
278 return ds.sem_otime;
279}