t_sem.c revision 1.4 1 1.4 thorpej /* $NetBSD: t_sem.c,v 1.4 2019/02/03 03:20:24 thorpej Exp $ */
2 1.1 jmmv
3 1.1 jmmv /*
4 1.4 thorpej * Copyright (c) 2008, 2010, 2019 The NetBSD Foundation, Inc.
5 1.1 jmmv * All rights reserved.
6 1.1 jmmv *
7 1.1 jmmv * Redistribution and use in source and binary forms, with or without
8 1.1 jmmv * modification, are permitted provided that the following conditions
9 1.1 jmmv * are met:
10 1.1 jmmv * 1. Redistributions of source code must retain the above copyright
11 1.1 jmmv * notice, this list of conditions and the following disclaimer.
12 1.1 jmmv * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 jmmv * notice, this list of conditions and the following disclaimer in the
14 1.1 jmmv * documentation and/or other materials provided with the distribution.
15 1.1 jmmv *
16 1.1 jmmv * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 jmmv * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 jmmv * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 jmmv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 jmmv * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 jmmv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 jmmv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 jmmv * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 jmmv * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 jmmv * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 jmmv * POSSIBILITY OF SUCH DAMAGE.
27 1.1 jmmv */
28 1.1 jmmv
29 1.1 jmmv /*
30 1.1 jmmv * Copyright (C) 2000 Jason Evans <jasone (at) freebsd.org>.
31 1.1 jmmv * All rights reserved.
32 1.1 jmmv *
33 1.1 jmmv * Redistribution and use in source and binary forms, with or without
34 1.1 jmmv * modification, are permitted provided that the following conditions
35 1.1 jmmv * are met:
36 1.1 jmmv * 1. Redistributions of source code must retain the above copyright
37 1.1 jmmv * notice(s), this list of conditions and the following disclaimer as
38 1.1 jmmv * the first lines of this file unmodified other than the possible
39 1.1 jmmv * addition of one or more copyright notices.
40 1.1 jmmv * 2. Redistributions in binary form must reproduce the above copyright
41 1.1 jmmv * notice(s), this list of conditions and the following disclaimer in
42 1.1 jmmv * the documentation and/or other materials provided with the
43 1.1 jmmv * distribution.
44 1.1 jmmv *
45 1.1 jmmv * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
46 1.1 jmmv * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 1.1 jmmv * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 1.1 jmmv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
49 1.1 jmmv * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50 1.1 jmmv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51 1.1 jmmv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
52 1.1 jmmv * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53 1.1 jmmv * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
54 1.1 jmmv * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
55 1.1 jmmv * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 1.1 jmmv */
57 1.1 jmmv
58 1.1 jmmv #include <sys/cdefs.h>
59 1.4 thorpej __COPYRIGHT("@(#) Copyright (c) 2008, 2010, 2019\
60 1.1 jmmv The NetBSD Foundation, inc. All rights reserved.");
61 1.4 thorpej __RCSID("$NetBSD: t_sem.c,v 1.4 2019/02/03 03:20:24 thorpej Exp $");
62 1.1 jmmv
63 1.4 thorpej #include <sys/mman.h>
64 1.1 jmmv #include <sys/wait.h>
65 1.1 jmmv
66 1.1 jmmv #include <errno.h>
67 1.1 jmmv #include <fcntl.h>
68 1.1 jmmv #include <semaphore.h>
69 1.1 jmmv #include <stdio.h>
70 1.1 jmmv #include <unistd.h>
71 1.1 jmmv
72 1.1 jmmv #include <atf-c.h>
73 1.1 jmmv
74 1.1 jmmv #define NCHILDREN 10
75 1.1 jmmv
76 1.3 christos ATF_TC_WITH_CLEANUP(basic);
77 1.1 jmmv ATF_TC_HEAD(basic, tc)
78 1.1 jmmv {
79 1.1 jmmv atf_tc_set_md_var(tc, "descr", "Checks basic functionality of POSIX "
80 1.1 jmmv "semaphores");
81 1.1 jmmv }
82 1.1 jmmv ATF_TC_BODY(basic, tc)
83 1.1 jmmv {
84 1.1 jmmv int val;
85 1.1 jmmv sem_t *sem_b;
86 1.1 jmmv
87 1.2 njoly if (sysconf(_SC_SEMAPHORES) == -1)
88 1.2 njoly atf_tc_skip("POSIX semaphores not supported");
89 1.1 jmmv
90 1.1 jmmv sem_b = sem_open("/sem_b", O_CREAT | O_EXCL, 0644, 0);
91 1.1 jmmv ATF_REQUIRE(sem_b != SEM_FAILED);
92 1.1 jmmv
93 1.1 jmmv ATF_REQUIRE_EQ(sem_getvalue(sem_b, &val), 0);
94 1.1 jmmv ATF_REQUIRE_EQ(val, 0);
95 1.1 jmmv
96 1.1 jmmv ATF_REQUIRE_EQ(sem_post(sem_b), 0);
97 1.1 jmmv ATF_REQUIRE_EQ(sem_getvalue(sem_b, &val), 0);
98 1.1 jmmv ATF_REQUIRE_EQ(val, 1);
99 1.1 jmmv
100 1.1 jmmv ATF_REQUIRE_EQ(sem_wait(sem_b), 0);
101 1.1 jmmv ATF_REQUIRE_EQ(sem_trywait(sem_b), -1);
102 1.1 jmmv ATF_REQUIRE_EQ(errno, EAGAIN);
103 1.1 jmmv ATF_REQUIRE_EQ(sem_post(sem_b), 0);
104 1.1 jmmv ATF_REQUIRE_EQ(sem_trywait(sem_b), 0);
105 1.1 jmmv ATF_REQUIRE_EQ(sem_post(sem_b), 0);
106 1.1 jmmv ATF_REQUIRE_EQ(sem_wait(sem_b), 0);
107 1.1 jmmv ATF_REQUIRE_EQ(sem_post(sem_b), 0);
108 1.1 jmmv
109 1.1 jmmv ATF_REQUIRE_EQ(sem_close(sem_b), 0);
110 1.1 jmmv ATF_REQUIRE_EQ(sem_unlink("/sem_b"), 0);
111 1.1 jmmv }
112 1.3 christos ATF_TC_CLEANUP(basic, tc)
113 1.3 christos {
114 1.3 christos (void)sem_unlink("/sem_b");
115 1.3 christos }
116 1.1 jmmv
117 1.3 christos ATF_TC_WITH_CLEANUP(child);
118 1.1 jmmv ATF_TC_HEAD(child, tc)
119 1.1 jmmv {
120 1.1 jmmv atf_tc_set_md_var(tc, "descr", "Checks using semaphores to synchronize "
121 1.1 jmmv "parent with multiple child processes");
122 1.1 jmmv }
123 1.1 jmmv ATF_TC_BODY(child, tc)
124 1.1 jmmv {
125 1.1 jmmv pid_t children[NCHILDREN];
126 1.1 jmmv unsigned i, j;
127 1.1 jmmv sem_t *sem_a;
128 1.1 jmmv int status;
129 1.1 jmmv
130 1.1 jmmv pid_t pid;
131 1.1 jmmv
132 1.3 christos if (sysconf(_SC_SEMAPHORES) == -1)
133 1.2 njoly atf_tc_skip("POSIX semaphores not supported");
134 1.2 njoly
135 1.1 jmmv sem_a = sem_open("/sem_a", O_CREAT | O_EXCL, 0644, 0);
136 1.1 jmmv ATF_REQUIRE(sem_a != SEM_FAILED);
137 1.1 jmmv
138 1.1 jmmv for (j = 1; j <= 2; j++) {
139 1.1 jmmv for (i = 0; i < NCHILDREN; i++) {
140 1.1 jmmv switch ((pid = fork())) {
141 1.1 jmmv case -1:
142 1.1 jmmv atf_tc_fail("fork() returned -1");
143 1.1 jmmv case 0:
144 1.1 jmmv printf("PID %d waiting for semaphore...\n",
145 1.1 jmmv getpid());
146 1.1 jmmv ATF_REQUIRE_MSG(sem_wait(sem_a) == 0,
147 1.1 jmmv "sem_wait failed; iteration %d", j);
148 1.1 jmmv printf("PID %d got semaphore\n", getpid());
149 1.1 jmmv _exit(0);
150 1.1 jmmv default:
151 1.1 jmmv children[i] = pid;
152 1.1 jmmv break;
153 1.1 jmmv }
154 1.1 jmmv }
155 1.1 jmmv
156 1.1 jmmv for (i = 0; i < NCHILDREN; i++) {
157 1.1 jmmv sleep(1);
158 1.1 jmmv printf("main loop %d: posting...\n", j);
159 1.1 jmmv ATF_REQUIRE_EQ(sem_post(sem_a), 0);
160 1.1 jmmv }
161 1.1 jmmv
162 1.1 jmmv for (i = 0; i < NCHILDREN; i++) {
163 1.1 jmmv ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]);
164 1.1 jmmv ATF_REQUIRE(WIFEXITED(status));
165 1.1 jmmv ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
166 1.1 jmmv }
167 1.1 jmmv }
168 1.1 jmmv
169 1.1 jmmv ATF_REQUIRE_EQ(sem_close(sem_a), 0);
170 1.1 jmmv ATF_REQUIRE_EQ(sem_unlink("/sem_a"), 0);
171 1.1 jmmv }
172 1.3 christos ATF_TC_CLEANUP(child, tc)
173 1.3 christos {
174 1.3 christos (void)sem_unlink("/sem_a");
175 1.3 christos }
176 1.1 jmmv
177 1.4 thorpej ATF_TC_WITH_CLEANUP(pshared);
178 1.4 thorpej ATF_TC_HEAD(pshared, tc)
179 1.4 thorpej {
180 1.4 thorpej atf_tc_set_md_var(tc, "descr", "Checks using pshared unnamed "
181 1.4 thorpej "semaphores to synchronize a master with multiple slave processes");
182 1.4 thorpej }
183 1.4 thorpej
184 1.4 thorpej struct shared_region {
185 1.4 thorpej sem_t the_sem;
186 1.4 thorpej };
187 1.4 thorpej
188 1.4 thorpej static struct shared_region *
189 1.4 thorpej get_shared_region(int o_flags)
190 1.4 thorpej {
191 1.4 thorpej
192 1.4 thorpej int fd = shm_open("/shm_semtest_a", o_flags, 0644);
193 1.4 thorpej ATF_REQUIRE(fd != -1);
194 1.4 thorpej
195 1.4 thorpej ATF_REQUIRE_EQ(ftruncate(fd, sizeof(struct shared_region)), 0);
196 1.4 thorpej
197 1.4 thorpej void *rv = mmap(NULL, sizeof(struct shared_region),
198 1.4 thorpej PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
199 1.4 thorpej ATF_REQUIRE(rv != MAP_FAILED);
200 1.4 thorpej
201 1.4 thorpej (void)close(fd);
202 1.4 thorpej
203 1.4 thorpej return rv;
204 1.4 thorpej }
205 1.4 thorpej
206 1.4 thorpej static void
207 1.4 thorpej put_shared_region(struct shared_region *r)
208 1.4 thorpej {
209 1.4 thorpej ATF_REQUIRE_EQ(munmap(r, sizeof(struct shared_region)), 0);
210 1.4 thorpej }
211 1.4 thorpej
212 1.4 thorpej ATF_TC_BODY(pshared, tc)
213 1.4 thorpej {
214 1.4 thorpej struct shared_region *master_region, *slave_region;;
215 1.4 thorpej
216 1.4 thorpej if (sysconf(_SC_SEMAPHORES) == -1)
217 1.4 thorpej atf_tc_skip("POSIX semaphores not supported");
218 1.4 thorpej
219 1.4 thorpej /*
220 1.4 thorpej * Create a shared memory region to contain the pshared
221 1.4 thorpej * semaphore, create the semaphore there, and then detach
222 1.4 thorpej * from the shared memory region to ensure that our child
223 1.4 thorpej * processes will be getting at it from scratch.
224 1.4 thorpej */
225 1.4 thorpej master_region = get_shared_region(O_RDWR | O_CREAT | O_EXCL);
226 1.4 thorpej ATF_REQUIRE(sem_init(&master_region->the_sem, 1, 0) == 0);
227 1.4 thorpej put_shared_region(master_region);
228 1.4 thorpej
229 1.4 thorpej /*
230 1.4 thorpej * Now execute a test that's essentially equivalent to the
231 1.4 thorpej * "child" test above, but using the pshared semaphore in the
232 1.4 thorpej * shared memory region.
233 1.4 thorpej */
234 1.4 thorpej
235 1.4 thorpej pid_t pid, children[NCHILDREN];
236 1.4 thorpej unsigned i, j;
237 1.4 thorpej int status;
238 1.4 thorpej
239 1.4 thorpej for (j = 1; j <= 2; j++) {
240 1.4 thorpej for (i = 0; i < NCHILDREN; i++) {
241 1.4 thorpej switch ((pid = fork())) {
242 1.4 thorpej case -1:
243 1.4 thorpej atf_tc_fail("fork() returned -1");
244 1.4 thorpej case 0:
245 1.4 thorpej slave_region = get_shared_region(O_RDWR);
246 1.4 thorpej printf("PID %d waiting for semaphore...\n",
247 1.4 thorpej getpid());
248 1.4 thorpej ATF_REQUIRE_MSG(sem_wait(&slave_region->the_sem)
249 1.4 thorpej == 0,
250 1.4 thorpej "sem_wait failed; iteration %d", j);
251 1.4 thorpej printf("PID %d got semaphore\n", getpid());
252 1.4 thorpej _exit(0);
253 1.4 thorpej default:
254 1.4 thorpej children[i] = pid;
255 1.4 thorpej break;
256 1.4 thorpej }
257 1.4 thorpej }
258 1.4 thorpej
259 1.4 thorpej master_region = get_shared_region(O_RDWR);
260 1.4 thorpej
261 1.4 thorpej for (i = 0; i < NCHILDREN; i++) {
262 1.4 thorpej sleep(1);
263 1.4 thorpej printf("main loop %d: posting...\n", j);
264 1.4 thorpej ATF_REQUIRE_EQ(sem_post(&master_region->the_sem), 0);
265 1.4 thorpej }
266 1.4 thorpej
267 1.4 thorpej put_shared_region(master_region);
268 1.4 thorpej
269 1.4 thorpej for (i = 0; i < NCHILDREN; i++) {
270 1.4 thorpej ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]);
271 1.4 thorpej ATF_REQUIRE(WIFEXITED(status));
272 1.4 thorpej ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
273 1.4 thorpej }
274 1.4 thorpej }
275 1.4 thorpej
276 1.4 thorpej master_region = get_shared_region(O_RDWR);
277 1.4 thorpej ATF_REQUIRE_EQ(sem_destroy(&master_region->the_sem), 0);
278 1.4 thorpej put_shared_region(master_region);
279 1.4 thorpej
280 1.4 thorpej ATF_REQUIRE_EQ(shm_unlink("/shm_semtest_a"), 0);
281 1.4 thorpej }
282 1.4 thorpej ATF_TC_CLEANUP(pshared, tc)
283 1.4 thorpej {
284 1.4 thorpej /*
285 1.4 thorpej * The kernel will g/c the pshared semaphore when the process that
286 1.4 thorpej * created it exits, so no need to include that in the cleanup here.
287 1.4 thorpej */
288 1.4 thorpej (void)shm_unlink("/shm_semtest_a");
289 1.4 thorpej }
290 1.4 thorpej
291 1.4 thorpej ATF_TC_WITH_CLEANUP(invalid_ops);
292 1.4 thorpej ATF_TC_HEAD(invalid_ops, tc)
293 1.4 thorpej {
294 1.4 thorpej atf_tc_set_md_var(tc, "descr", "Validates behavior when calling "
295 1.4 thorpej "bad operations for the semaphore type");
296 1.4 thorpej }
297 1.4 thorpej ATF_TC_BODY(invalid_ops, tc)
298 1.4 thorpej {
299 1.4 thorpej sem_t *sem;
300 1.4 thorpej sem_t the_sem;
301 1.4 thorpej
302 1.4 thorpej sem = sem_open("/sem_c", O_CREAT | O_EXCL, 0644, 0);
303 1.4 thorpej ATF_REQUIRE(sem != SEM_FAILED);
304 1.4 thorpej ATF_REQUIRE(sem_destroy(sem) == -1 && errno == EINVAL);
305 1.4 thorpej ATF_REQUIRE_EQ(sem_close(sem), 0);
306 1.4 thorpej
307 1.4 thorpej ATF_REQUIRE_EQ(sem_init(&the_sem, 0, 0), 0);
308 1.4 thorpej ATF_REQUIRE(sem_close(&the_sem) == -1 && errno == EINVAL);
309 1.4 thorpej ATF_REQUIRE_EQ(sem_destroy(&the_sem), 0);
310 1.4 thorpej }
311 1.4 thorpej ATF_TC_CLEANUP(invalid_ops, tc)
312 1.4 thorpej {
313 1.4 thorpej (void)sem_unlink("/sem_c");
314 1.4 thorpej }
315 1.4 thorpej
316 1.1 jmmv ATF_TP_ADD_TCS(tp)
317 1.1 jmmv {
318 1.1 jmmv
319 1.1 jmmv ATF_TP_ADD_TC(tp, basic);
320 1.1 jmmv ATF_TP_ADD_TC(tp, child);
321 1.4 thorpej ATF_TP_ADD_TC(tp, pshared);
322 1.4 thorpej ATF_TP_ADD_TC(tp, invalid_ops);
323 1.1 jmmv
324 1.1 jmmv return atf_no_error();
325 1.1 jmmv }
326