Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <mman.h>
28#include <pthread.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33
34static int mutex_test();
35static int detached_test();
36static int priority_test();
37static int stack_size_test();
38static int set_stack_test();
39
40int main(int argc, char** argv)
41{
42 if (argc == 2 && *argv[1] == 'm')
43 return mutex_test();
44 if (argc == 2 && *argv[1] == 'd')
45 return detached_test();
46 if (argc == 2 && *argv[1] == 'p')
47 return priority_test();
48 if (argc == 2 && *argv[1] == 's')
49 return stack_size_test();
50 if (argc == 2 && *argv[1] == 'x')
51 return set_stack_test();
52
53 printf("Hello from the first thread!\n");
54 pthread_t thread_id;
55 int rc = pthread_create(
56 &thread_id, nullptr, [](void*) -> void* {
57 printf("Hi there, from the second thread!\n");
58 pthread_exit((void*)0xDEADBEEF);
59 return nullptr;
60 },
61 nullptr);
62 if (rc < 0) {
63 perror("pthread_create");
64 return 1;
65 }
66 void* retval;
67 rc = pthread_join(thread_id, &retval);
68 if (rc < 0) {
69 perror("pthread_join");
70 return 1;
71 }
72 printf("Okay, joined and got retval=%p\n", retval);
73 return 0;
74}
75
76static pthread_mutex_t mutex;
77
78int mutex_test()
79{
80 int rc = pthread_mutex_init(&mutex, nullptr);
81 if (rc < 0) {
82 perror("pthread_mutex_init");
83 return 1;
84 }
85 pthread_t thread_id;
86 rc = pthread_create(
87 &thread_id, nullptr, [](void*) -> void* {
88 printf("I'm the secondary thread :^)\n");
89 for (;;) {
90 pthread_mutex_lock(&mutex);
91 printf("Second thread stole mutex\n");
92 sleep(1);
93 printf("Second thread giving back mutex\n");
94 pthread_mutex_unlock(&mutex);
95 sleep(1);
96 }
97 pthread_exit((void*)0xDEADBEEF);
98 return nullptr;
99 },
100 nullptr);
101 if (rc < 0) {
102 perror("pthread_create");
103 return 1;
104 }
105 for (;;) {
106 pthread_mutex_lock(&mutex);
107 printf("Obnoxious spam!\n");
108 pthread_mutex_unlock(&mutex);
109 usleep(10000);
110 }
111 return 0;
112}
113
114int detached_test()
115{
116 pthread_attr_t attributes;
117 int rc = pthread_attr_init(&attributes);
118 if (rc != 0) {
119 printf("pthread_attr_setdetachstate: %s\n", strerror(rc));
120 return 1;
121 }
122
123 int detach_state = 99; // clearly invalid
124 rc = pthread_attr_getdetachstate(&attributes, &detach_state);
125 if (rc != 0) {
126 printf("pthread_attr_setdetachstate: %s\n", strerror(rc));
127 return 2;
128 }
129 printf("Default detach state: %s\n", detach_state == PTHREAD_CREATE_JOINABLE ? "joinable" : "detached");
130
131 detach_state = PTHREAD_CREATE_DETACHED;
132 rc = pthread_attr_setdetachstate(&attributes, detach_state);
133 if (rc != 0) {
134 printf("pthread_attr_setdetachstate: %s\n", strerror(rc));
135 return 3;
136 }
137 printf("Set detach state on new thread to detached\n");
138
139 pthread_t thread_id;
140 rc = pthread_create(
141 &thread_id, &attributes, [](void*) -> void* {
142 printf("I'm the secondary thread :^)\n");
143 sleep(1);
144 pthread_exit((void*)0xDEADBEEF);
145 return nullptr;
146 },
147 nullptr);
148 if (rc < 0) {
149 perror("pthread_create");
150 return 4;
151 }
152
153 void* ret_val;
154 errno = 0;
155 rc = pthread_join(thread_id, &ret_val);
156 if (rc < 0 && errno != EINVAL) {
157 perror("pthread_join");
158 return 5;
159 }
160 if (errno != EINVAL) {
161 printf("Expected EINVAL! Thread was joinable?\n");
162 return 6;
163 }
164
165 sleep(2);
166 printf("Thread was created detached. I sure hope it exited on its own.\n");
167
168 rc = pthread_attr_destroy(&attributes);
169 if (rc != 0) {
170 printf("pthread_attr_setdetachstate: %s\n", strerror(rc));
171 return 7;
172 }
173
174 return 0;
175}
176
177int priority_test()
178{
179 pthread_attr_t attributes;
180 int rc = pthread_attr_init(&attributes);
181 if (rc != 0) {
182 printf("pthread_attr_init: %s\n", strerror(rc));
183 return 1;
184 }
185
186 struct sched_param sched_params;
187 rc = pthread_attr_getschedparam(&attributes, &sched_params);
188 if (rc != 0) {
189 printf("pthread_attr_getschedparam: %s\n", strerror(rc));
190 return 2;
191 }
192 printf("Default priority: %d\n", sched_params.sched_priority);
193
194 sched_params.sched_priority = 3;
195 rc = pthread_attr_setschedparam(&attributes, &sched_params);
196 if (rc != 0) {
197 printf("pthread_attr_setschedparam: %s\n", strerror(rc));
198 return 3;
199 }
200 printf("Set thread priority to 3\n");
201
202 pthread_t thread_id;
203 rc = pthread_create(
204 &thread_id, &attributes, [](void*) -> void* {
205 printf("I'm the secondary thread :^)\n");
206 sleep(1);
207 pthread_exit((void*)0xDEADBEEF);
208 return nullptr;
209 },
210 nullptr);
211 if (rc < 0) {
212 perror("pthread_create");
213 return 4;
214 }
215
216 rc = pthread_join(thread_id, nullptr);
217 if (rc < 0) {
218 perror("pthread_join");
219 return 5;
220 }
221
222 rc = pthread_attr_destroy(&attributes);
223 if (rc != 0) {
224 printf("pthread_attr_destroy: %s\n", strerror(rc));
225 return 6;
226 }
227
228 return 0;
229}
230
231int stack_size_test()
232{
233 pthread_attr_t attributes;
234 int rc = pthread_attr_init(&attributes);
235 if (rc != 0) {
236 printf("pthread_attr_init: %s\n", strerror(rc));
237 return 1;
238 }
239
240 size_t stack_size;
241 rc = pthread_attr_getstacksize(&attributes, &stack_size);
242 if (rc != 0) {
243 printf("pthread_attr_getstacksize: %s\n", strerror(rc));
244 return 2;
245 }
246 printf("Default stack size: %zu\n", stack_size);
247
248 stack_size = 8 * 1024 * 1024;
249 rc = pthread_attr_setstacksize(&attributes, stack_size);
250 if (rc != 0) {
251 printf("pthread_attr_setstacksize: %s\n", strerror(rc));
252 return 3;
253 }
254 printf("Set thread stack size to 8 MB\n");
255
256 pthread_t thread_id;
257 rc = pthread_create(
258 &thread_id, &attributes, [](void*) -> void* {
259 printf("I'm the secondary thread :^)\n");
260 sleep(1);
261 pthread_exit((void*)0xDEADBEEF);
262 return nullptr;
263 },
264 nullptr);
265 if (rc < 0) {
266 perror("pthread_create");
267 return 4;
268 }
269
270 rc = pthread_join(thread_id, nullptr);
271 if (rc < 0) {
272 perror("pthread_join");
273 return 5;
274 }
275
276 rc = pthread_attr_destroy(&attributes);
277 if (rc != 0) {
278 printf("pthread_attr_destroy: %s\n", strerror(rc));
279 return 6;
280 }
281
282 return 0;
283}
284
285int set_stack_test()
286{
287 pthread_attr_t attributes;
288 int rc = pthread_attr_init(&attributes);
289 if (rc < 0) {
290 printf("pthread_attr_init: %s\n", strerror(rc));
291 return 1;
292 }
293
294 size_t stack_size = 8 * 1024 * 1024;
295 void* stack_addr = mmap_with_name(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Cool stack");
296
297 if (!stack_addr) {
298 perror("mmap_with_name");
299 return -1;
300 }
301
302 rc = pthread_attr_setstack(&attributes, stack_addr, stack_size);
303 if (rc != 0) {
304 printf("pthread_attr_setstack: %s\n", strerror(rc));
305 return 2;
306 }
307 printf("Set thread stack to %p, size %zu\n", stack_addr, stack_size);
308
309 size_t stack_size_verify;
310 void* stack_addr_verify;
311
312 rc = pthread_attr_getstack(&attributes, &stack_addr_verify, &stack_size_verify);
313 if (rc != 0) {
314 printf("pthread_attr_getstack: %s\n", strerror(rc));
315 return 3;
316 }
317
318 if (stack_addr != stack_addr_verify || stack_size != stack_size_verify) {
319 printf("Stack address and size don't match! addr: %p %p, size: %zu %zu\n", stack_addr, stack_addr_verify, stack_size, stack_size_verify);
320 return 4;
321 }
322
323 pthread_t thread_id;
324 rc = pthread_create(
325 &thread_id, &attributes, [](void*) -> void* {
326 printf("I'm the secondary thread :^)\n");
327 sleep(1);
328 pthread_exit((void*)0xDEADBEEF);
329 return nullptr;
330 },
331 nullptr);
332 if (rc < 0) {
333 perror("pthread_create");
334 return 5;
335 }
336
337 rc = pthread_join(thread_id, nullptr);
338 if (rc < 0) {
339 perror("pthread_join");
340 return 6;
341 }
342
343 rc = pthread_attr_destroy(&attributes);
344 if (rc != 0) {
345 printf("pthread_attr_destroy: %s\n", strerror(rc));
346 return 7;
347 }
348
349 return 0;
350}