1 | /* Test SIGEV_THREAD handling for POSIX message queues. |
2 | Copyright (C) 2004-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <errno.h> |
20 | #include <mqueue.h> |
21 | #include <signal.h> |
22 | #include <stddef.h> |
23 | #include <stdint.h> |
24 | #include <stdio.h> |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | #include <sys/mman.h> |
28 | #include <sys/wait.h> |
29 | #include <unistd.h> |
30 | #include <support/check.h> |
31 | |
32 | #if _POSIX_THREADS |
33 | # include <pthread.h> |
34 | |
35 | static pid_t pid; |
36 | static mqd_t m; |
37 | static const char message[] = "hello" ; |
38 | |
39 | # define MAXMSG 10 |
40 | # define MSGSIZE 10 |
41 | # define UNIQUE 42 |
42 | |
43 | |
44 | static void |
45 | fct (union sigval s) |
46 | { |
47 | /* Put the mq in non-blocking mode. */ |
48 | struct mq_attr attr; |
49 | if (mq_getattr (mqdes: m, mqstat: &attr) != 0) |
50 | { |
51 | printf (format: "%s: mq_getattr failed: %m\n" , __FUNCTION__); |
52 | exit (1); |
53 | } |
54 | attr.mq_flags |= O_NONBLOCK; |
55 | if (mq_setattr (m, &attr, NULL) != 0) |
56 | { |
57 | printf (format: "%s: mq_setattr failed: %m\n" , __FUNCTION__); |
58 | exit (1); |
59 | } |
60 | |
61 | /* Check the values. */ |
62 | if (attr.mq_maxmsg != MAXMSG) |
63 | { |
64 | printf (format: "%s: mq_maxmsg wrong: is %jd, expecte %d\n" , |
65 | __FUNCTION__, (intmax_t) attr.mq_maxmsg, MAXMSG); |
66 | exit (1); |
67 | } |
68 | if (attr.mq_msgsize != MAXMSG) |
69 | { |
70 | printf (format: "%s: mq_msgsize wrong: is %jd, expecte %d\n" , |
71 | __FUNCTION__, (intmax_t) attr.mq_msgsize, MSGSIZE); |
72 | exit (1); |
73 | } |
74 | |
75 | /* Read the message. */ |
76 | char buf[attr.mq_msgsize]; |
77 | ssize_t n = TEMP_FAILURE_RETRY (mq_receive (m, buf, attr.mq_msgsize, NULL)); |
78 | if (n != sizeof (message)) |
79 | { |
80 | printf (format: "%s: length of message wrong: is %zd, expected %zu\n" , |
81 | __FUNCTION__, n, sizeof (message)); |
82 | exit (1); |
83 | } |
84 | if (memcmp (buf, message, sizeof (message)) != 0) |
85 | { |
86 | printf (format: "%s: message wrong: is \"%s\", expected \"%s\"\n" , |
87 | __FUNCTION__, buf, message); |
88 | exit (1); |
89 | } |
90 | |
91 | exit (UNIQUE); |
92 | } |
93 | |
94 | |
95 | int |
96 | do_test (void) |
97 | { |
98 | char tmpfname[] = "/tmp/tst-mqueue3-barrier.XXXXXX" ; |
99 | int fd = mkstemp (template: tmpfname); |
100 | if (fd == -1) |
101 | { |
102 | printf (format: "cannot open temporary file: %m\n" ); |
103 | return 1; |
104 | } |
105 | |
106 | /* Make sure it is always removed. */ |
107 | unlink (name: tmpfname); |
108 | |
109 | /* Create one page of data. */ |
110 | size_t ps = sysconf (_SC_PAGESIZE); |
111 | char data[ps]; |
112 | memset (data, '\0', ps); |
113 | |
114 | /* Write the data to the file. */ |
115 | if (write (fd, data, ps) != (ssize_t) ps) |
116 | { |
117 | puts (s: "short write" ); |
118 | return 1; |
119 | } |
120 | |
121 | void *mem = mmap (NULL, len: ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd: fd, offset: 0); |
122 | if (mem == MAP_FAILED) |
123 | { |
124 | printf (format: "mmap failed: %m\n" ); |
125 | return 1; |
126 | } |
127 | |
128 | pthread_barrier_t *b; |
129 | b = (pthread_barrier_t *) (((uintptr_t) mem + __alignof (pthread_barrier_t)) |
130 | & ~(__alignof (pthread_barrier_t) - 1)); |
131 | |
132 | pthread_barrierattr_t a; |
133 | if (pthread_barrierattr_init (attr: &a) != 0) |
134 | { |
135 | puts (s: "barrierattr_init failed" ); |
136 | return 1; |
137 | } |
138 | |
139 | if (pthread_barrierattr_setpshared (attr: &a, PTHREAD_PROCESS_SHARED) != 0) |
140 | { |
141 | puts (s: "barrierattr_setpshared failed, could not test" ); |
142 | return 0; |
143 | } |
144 | |
145 | if (pthread_barrier_init (barrier: b, attr: &a, count: 2) != 0) |
146 | { |
147 | puts (s: "barrier_init failed" ); |
148 | return 1; |
149 | } |
150 | |
151 | if (pthread_barrierattr_destroy (attr: &a) != 0) |
152 | { |
153 | puts (s: "barrierattr_destroy failed" ); |
154 | return 1; |
155 | } |
156 | |
157 | /* Name for the message queue. */ |
158 | char mqname[sizeof ("/tst-mqueue3-" ) + 3 * sizeof (pid_t)]; |
159 | snprintf (s: mqname, maxlen: sizeof (mqname) - 1, format: "/tst-mqueue3-%ld" , |
160 | (long int) getpid ()); |
161 | |
162 | /* Create the message queue. */ |
163 | struct mq_attr attr = { .mq_maxmsg = MAXMSG, .mq_msgsize = MSGSIZE }; |
164 | m = mq_open (name: mqname, O_CREAT | O_EXCL | O_RDWR, 0600, &attr); |
165 | |
166 | if (m == -1) |
167 | { |
168 | if (errno == ENOSYS) |
169 | FAIL_UNSUPPORTED ("mq_open not supported" ); |
170 | |
171 | printf (format: "mq_open failed with: %m\n" ); |
172 | return 1; |
173 | } |
174 | |
175 | /* Unlink the message queue right away. */ |
176 | if (mq_unlink (name: mqname) != 0) |
177 | { |
178 | puts (s: "mq_unlink failed" ); |
179 | return 1; |
180 | } |
181 | |
182 | pid = fork (); |
183 | if (pid == -1) |
184 | { |
185 | puts (s: "fork failed" ); |
186 | return 1; |
187 | } |
188 | if (pid == 0) |
189 | { |
190 | /* Request notification via thread. */ |
191 | struct sigevent ev; |
192 | ev.sigev_notify = SIGEV_THREAD; |
193 | ev.sigev_notify_function = fct; |
194 | ev.sigev_value.sival_ptr = NULL; |
195 | ev.sigev_notify_attributes = NULL; |
196 | |
197 | /* Tell the kernel. */ |
198 | if (mq_notify (mqdes: m,notification: &ev) != 0) |
199 | { |
200 | puts (s: "mq_notify failed" ); |
201 | exit (1); |
202 | } |
203 | |
204 | /* Tell the parent we are ready. */ |
205 | (void) pthread_barrier_wait (barrier: b); |
206 | |
207 | /* Make sure the process goes away eventually. */ |
208 | alarm (10); |
209 | |
210 | /* Do nothing forever. */ |
211 | while (1) |
212 | pause (); |
213 | } |
214 | |
215 | /* Wait for the child process to register to notification method. */ |
216 | (void) pthread_barrier_wait (barrier: b); |
217 | |
218 | /* Send the message. */ |
219 | if (mq_send (mqdes: m, msg_ptr: message, msg_len: sizeof (message), msg_prio: 1) != 0) |
220 | { |
221 | kill (pid: pid, SIGKILL); |
222 | puts (s: "mq_send failed" ); |
223 | return 1; |
224 | } |
225 | |
226 | int r; |
227 | if (TEMP_FAILURE_RETRY (waitpid (pid, &r, 0)) != pid) |
228 | { |
229 | kill (pid: pid, SIGKILL); |
230 | puts (s: "waitpid failed" ); |
231 | return 1; |
232 | } |
233 | |
234 | return WIFEXITED (r) && WEXITSTATUS (r) == UNIQUE ? 0 : 1; |
235 | } |
236 | # define TEST_FUNCTION do_test () |
237 | #else |
238 | # define TEST_FUNCTION 0 |
239 | #endif |
240 | |
241 | #include "../test-skeleton.c" |
242 | |