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 <AK/Assertions.h>
28#include <AK/Atomic.h>
29#include <AK/StdLibExtras.h>
30#include <Kernel/Syscall.h>
31#include <limits.h>
32#include <pthread.h>
33#include <serenity.h>
34#include <signal.h>
35#include <stdio.h>
36#include <string.h>
37#include <sys/mman.h>
38#include <time.h>
39#include <unistd.h>
40
41//#define PTHREAD_DEBUG
42
43namespace {
44using PthreadAttrImpl = Syscall::SC_create_thread_params;
45} // end anonymous namespace
46
47constexpr size_t required_stack_alignment = 4 * MB;
48constexpr size_t highest_reasonable_guard_size = 32 * PAGE_SIZE;
49constexpr size_t highest_reasonable_stack_size = 8 * MB; // That's the default in Ubuntu?
50
51extern "C" {
52
53static int create_thread(void* (*entry)(void*), void* argument, void* thread_params)
54{
55 return syscall(SC_create_thread, entry, argument, thread_params);
56}
57
58static void exit_thread(void* code)
59{
60 syscall(SC_exit_thread, code);
61 ASSERT_NOT_REACHED();
62}
63
64int pthread_self()
65{
66 return gettid();
67}
68
69int pthread_create(pthread_t* thread, pthread_attr_t* attributes, void* (*start_routine)(void*), void* argument_to_start_routine)
70{
71 if (!thread)
72 return -EINVAL;
73
74 PthreadAttrImpl default_attributes {};
75 PthreadAttrImpl** arg_attributes = reinterpret_cast<PthreadAttrImpl**>(attributes);
76
77 PthreadAttrImpl* used_attributes = arg_attributes ? *arg_attributes : &default_attributes;
78
79 if (!used_attributes->m_stack_location) {
80 // adjust stack size, user might have called setstacksize, which has no restrictions on size/alignment
81 if (0 != (used_attributes->m_stack_size % required_stack_alignment))
82 used_attributes->m_stack_size += required_stack_alignment - (used_attributes->m_stack_size % required_stack_alignment);
83
84 used_attributes->m_stack_location = mmap_with_name(nullptr, used_attributes->m_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Thread stack");
85 if (!used_attributes->m_stack_location)
86 return -1;
87 }
88
89#ifdef PTHREAD_DEBUG
90 dbgprintf("pthread_create: Creating thread with attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
91 used_attributes,
92 (PTHREAD_CREATE_JOINABLE == used_attributes->m_detach_state) ? "joinable" : "detached",
93 used_attributes->m_schedule_priority,
94 used_attributes->m_guard_page_size,
95 used_attributes->m_stack_size,
96 used_attributes->m_stack_location);
97#endif
98
99 int rc = create_thread(start_routine, argument_to_start_routine, used_attributes);
100 if (rc < 0)
101 return rc;
102 *thread = rc;
103 return 0;
104}
105
106void pthread_exit(void* value_ptr)
107{
108 exit_thread(value_ptr);
109}
110
111int pthread_join(pthread_t thread, void** exit_value_ptr)
112{
113 return syscall(SC_join_thread, thread, exit_value_ptr);
114}
115
116int pthread_detach(pthread_t thread)
117{
118 return syscall(SC_detach_thread, thread);
119}
120
121int pthread_sigmask(int how, const sigset_t* set, sigset_t* old_set)
122{
123 if (sigprocmask(how, set, old_set))
124 return errno;
125 return 0;
126}
127
128int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attributes)
129{
130 mutex->lock = 0;
131 mutex->owner = 0;
132 mutex->level = 0;
133 mutex->type = attributes ? attributes->type : PTHREAD_MUTEX_NORMAL;
134 return 0;
135}
136
137int pthread_mutex_destroy(pthread_mutex_t*)
138{
139 return 0;
140}
141
142int pthread_mutex_lock(pthread_mutex_t* mutex)
143{
144 auto& atomic = reinterpret_cast<Atomic<u32>&>(mutex->lock);
145 pthread_t this_thread = pthread_self();
146 for (;;) {
147 u32 expected = false;
148 if (!atomic.compare_exchange_strong(expected, true, AK::memory_order_acq_rel)) {
149 if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->owner == this_thread) {
150 mutex->level++;
151 return 0;
152 }
153 sched_yield();
154 continue;
155 }
156 mutex->owner = this_thread;
157 mutex->level = 0;
158 return 0;
159 }
160}
161
162int pthread_mutex_trylock(pthread_mutex_t* mutex)
163{
164 auto& atomic = reinterpret_cast<Atomic<u32>&>(mutex->lock);
165 u32 expected = false;
166 if (!atomic.compare_exchange_strong(expected, true, AK::memory_order_acq_rel)) {
167 if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->owner == pthread_self()) {
168 mutex->level++;
169 return 0;
170 }
171 return EBUSY;
172 }
173 mutex->owner = pthread_self();
174 mutex->level = 0;
175 return 0;
176}
177
178int pthread_mutex_unlock(pthread_mutex_t* mutex)
179{
180 if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->level > 0) {
181 mutex->level--;
182 return 0;
183 }
184 mutex->owner = 0;
185 mutex->lock = 0;
186 return 0;
187}
188
189int pthread_mutexattr_init(pthread_mutexattr_t* attr)
190{
191 attr->type = PTHREAD_MUTEX_NORMAL;
192 return 0;
193}
194
195int pthread_mutexattr_destroy(pthread_mutexattr_t*)
196{
197 return 0;
198}
199
200int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type)
201{
202 if (!attr)
203 return EINVAL;
204 if (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE)
205 return EINVAL;
206 attr->type = type;
207 return 0;
208}
209
210int pthread_attr_init(pthread_attr_t* attributes)
211{
212 auto* impl = new PthreadAttrImpl {};
213 *attributes = impl;
214
215#ifdef PTHREAD_DEBUG
216 dbgprintf("pthread_attr_init: New thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
217 impl,
218 (PTHREAD_CREATE_JOINABLE == impl->m_detach_state) ? "joinable" : "detached",
219 impl->m_schedule_priority,
220 impl->m_guard_page_size,
221 impl->m_stack_size,
222 impl->m_stack_location);
223#endif
224
225 return 0;
226}
227
228int pthread_attr_destroy(pthread_attr_t* attributes)
229{
230 auto* attributes_impl = *(reinterpret_cast<PthreadAttrImpl**>(attributes));
231 delete attributes_impl;
232 return 0;
233}
234
235int pthread_attr_getdetachstate(const pthread_attr_t* attributes, int* p_detach_state)
236{
237 auto* attributes_impl = *(reinterpret_cast<const PthreadAttrImpl* const*>(attributes));
238
239 if (!attributes_impl || !p_detach_state)
240 return EINVAL;
241
242 *p_detach_state = attributes_impl->m_detach_state;
243 return 0;
244}
245
246int pthread_attr_setdetachstate(pthread_attr_t* attributes, int detach_state)
247{
248 auto* attributes_impl = *(reinterpret_cast<PthreadAttrImpl**>(attributes));
249
250 if (!attributes_impl)
251 return EINVAL;
252
253 if ((PTHREAD_CREATE_JOINABLE != detach_state) || PTHREAD_CREATE_DETACHED != detach_state)
254 return EINVAL;
255
256 attributes_impl->m_detach_state = detach_state;
257
258#ifdef PTHREAD_DEBUG
259 dbgprintf("pthread_attr_setdetachstate: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
260 attributes_impl,
261 (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached",
262 attributes_impl->m_schedule_priority,
263 attributes_impl->m_guard_page_size,
264 attributes_impl->m_stack_size,
265 attributes_impl->m_stack_location);
266#endif
267
268 return 0;
269}
270
271int pthread_attr_getguardsize(const pthread_attr_t* attributes, size_t* p_guard_size)
272{
273 auto* attributes_impl = *(reinterpret_cast<const PthreadAttrImpl* const*>(attributes));
274
275 if (!attributes_impl || !p_guard_size)
276 return EINVAL;
277
278 *p_guard_size = attributes_impl->m_reported_guard_page_size;
279 return 0;
280}
281
282int pthread_attr_setguardsize(pthread_attr_t* attributes, size_t guard_size)
283{
284 auto* attributes_impl = *(reinterpret_cast<PthreadAttrImpl**>(attributes));
285
286 if (!attributes_impl)
287 return EINVAL;
288
289 size_t actual_guard_size = guard_size;
290 // round up
291 if (0 != (guard_size % PAGE_SIZE))
292 actual_guard_size += PAGE_SIZE - (guard_size % PAGE_SIZE);
293
294 // what is the user even doing?
295 if (actual_guard_size > highest_reasonable_guard_size) {
296 return EINVAL;
297 }
298
299 attributes_impl->m_guard_page_size = actual_guard_size;
300 attributes_impl->m_reported_guard_page_size = guard_size; // POSIX, why?
301
302#ifdef PTHREAD_DEBUG
303 dbgprintf("pthread_attr_setguardsize: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
304 attributes_impl,
305 (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached",
306 attributes_impl->m_schedule_priority,
307 attributes_impl->m_guard_page_size,
308 attributes_impl->m_stack_size,
309 attributes_impl->m_stack_location);
310#endif
311
312 return 0;
313}
314
315int pthread_attr_getschedparam(const pthread_attr_t* attributes, struct sched_param* p_sched_param)
316{
317 auto* attributes_impl = *(reinterpret_cast<const PthreadAttrImpl* const*>(attributes));
318
319 if (!attributes_impl || !p_sched_param)
320 return EINVAL;
321
322 p_sched_param->sched_priority = attributes_impl->m_schedule_priority;
323 return 0;
324}
325
326int pthread_attr_setschedparam(pthread_attr_t* attributes, const struct sched_param* p_sched_param)
327{
328 auto* attributes_impl = *(reinterpret_cast<PthreadAttrImpl**>(attributes));
329 if (!attributes_impl || !p_sched_param)
330 return EINVAL;
331
332 if (p_sched_param->sched_priority < THREAD_PRIORITY_MIN || p_sched_param->sched_priority > THREAD_PRIORITY_MAX)
333 return ENOTSUP;
334
335 attributes_impl->m_schedule_priority = p_sched_param->sched_priority;
336
337#ifdef PTHREAD_DEBUG
338 dbgprintf("pthread_attr_setschedparam: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
339 attributes_impl,
340 (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached",
341 attributes_impl->m_schedule_priority,
342 attributes_impl->m_guard_page_size,
343 attributes_impl->m_stack_size,
344 attributes_impl->m_stack_location);
345#endif
346
347 return 0;
348}
349
350int pthread_attr_getstack(const pthread_attr_t* attributes, void** p_stack_ptr, size_t* p_stack_size)
351{
352 auto* attributes_impl = *(reinterpret_cast<const PthreadAttrImpl* const*>(attributes));
353
354 if (!attributes_impl || !p_stack_ptr || !p_stack_size)
355 return EINVAL;
356
357 *p_stack_ptr = attributes_impl->m_stack_location;
358 *p_stack_size = attributes_impl->m_stack_size;
359
360 return 0;
361}
362
363int pthread_attr_setstack(pthread_attr_t* attributes, void* p_stack, size_t stack_size)
364{
365 auto* attributes_impl = *(reinterpret_cast<PthreadAttrImpl**>(attributes));
366
367 if (!attributes_impl || !p_stack)
368 return EINVAL;
369
370 // Check for required alignment on size
371 if (0 != (stack_size % required_stack_alignment))
372 return EINVAL;
373
374 // FIXME: Check for required alignment on pointer?
375
376 // FIXME: "[EACCES] The stack page(s) described by stackaddr and stacksize are not both readable and writable by the thread."
377 // Have to check that the whole range is mapped to this process/thread? Can we defer this to create_thread?
378
379 attributes_impl->m_stack_size = stack_size;
380 attributes_impl->m_stack_location = p_stack;
381
382#ifdef PTHREAD_DEBUG
383 dbgprintf("pthread_attr_setstack: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
384 attributes_impl,
385 (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached",
386 attributes_impl->m_schedule_priority,
387 attributes_impl->m_guard_page_size,
388 attributes_impl->m_stack_size,
389 attributes_impl->m_stack_location);
390#endif
391
392 return 0;
393}
394
395int pthread_attr_getstacksize(const pthread_attr_t* attributes, size_t* p_stack_size)
396{
397 auto* attributes_impl = *(reinterpret_cast<const PthreadAttrImpl* const*>(attributes));
398
399 if (!attributes_impl || !p_stack_size)
400 return EINVAL;
401
402 *p_stack_size = attributes_impl->m_stack_size;
403 return 0;
404}
405
406int pthread_attr_setstacksize(pthread_attr_t* attributes, size_t stack_size)
407{
408 auto* attributes_impl = *(reinterpret_cast<PthreadAttrImpl**>(attributes));
409
410 if (!attributes_impl)
411 return EINVAL;
412
413 if ((stack_size < PTHREAD_STACK_MIN) || stack_size > highest_reasonable_stack_size)
414 return EINVAL;
415
416 attributes_impl->m_stack_size = stack_size;
417
418#ifdef PTHREAD_DEBUG
419 dbgprintf("pthread_attr_setstacksize: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n",
420 attributes_impl,
421 (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached",
422 attributes_impl->m_schedule_priority,
423 attributes_impl->m_guard_page_size,
424 attributes_impl->m_stack_size,
425 attributes_impl->m_stack_location);
426#endif
427
428 return 0;
429}
430
431int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param)
432{
433 (void)thread;
434 (void)policy;
435 (void)param;
436 return 0;
437}
438
439int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param)
440{
441 (void)thread;
442 (void)policy;
443 (void)param;
444 return 0;
445}
446
447int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr)
448{
449 cond->value = 0;
450 cond->previous = 0;
451 cond->clockid = attr ? attr->clockid : CLOCK_MONOTONIC;
452 return 0;
453}
454
455int pthread_cond_destroy(pthread_cond_t*)
456{
457 return 0;
458}
459
460int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex)
461{
462 i32 value = cond->value;
463 cond->previous = value;
464 pthread_mutex_unlock(mutex);
465 int rc = futex(&cond->value, FUTEX_WAIT, value, nullptr);
466 ASSERT(rc == 0);
467 pthread_mutex_lock(mutex);
468 return 0;
469}
470
471int pthread_condattr_init(pthread_condattr_t* attr)
472{
473 attr->clockid = CLOCK_MONOTONIC;
474 return 0;
475}
476
477int pthread_condattr_destroy(pthread_condattr_t*)
478{
479 return 0;
480}
481
482int pthread_condattr_setclock(pthread_condattr_t* attr, clockid_t clock)
483{
484 attr->clockid = clock;
485 return 0;
486}
487
488int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime)
489{
490 // FIXME: Implement timeout.
491 (void)abstime;
492 pthread_cond_wait(cond, mutex);
493 return 0;
494}
495
496int pthread_cond_signal(pthread_cond_t* cond)
497{
498 u32 value = cond->previous + 1;
499 cond->value = value;
500 int rc = futex(&cond->value, FUTEX_WAKE, 1, nullptr);
501 ASSERT(rc == 0);
502 return 0;
503}
504
505int pthread_cond_broadcast(pthread_cond_t* cond)
506{
507 u32 value = cond->previous + 1;
508 cond->value = value;
509 int rc = futex(&cond->value, FUTEX_WAKE, INT32_MAX, nullptr);
510 ASSERT(rc == 0);
511 return 0;
512}
513
514static const int max_keys = 64;
515
516typedef void (*KeyDestructor)(void*);
517
518struct KeyTable {
519 // FIXME: Invoke key destructors on thread exit!
520 KeyDestructor destructors[64] { nullptr };
521 int next { 0 };
522 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
523};
524
525struct SpecificTable {
526 void* values[64] { nullptr };
527};
528
529static KeyTable s_keys;
530
531__thread SpecificTable t_specifics;
532
533int pthread_key_create(pthread_key_t* key, KeyDestructor destructor)
534{
535 int ret = 0;
536 pthread_mutex_lock(&s_keys.mutex);
537 if (s_keys.next >= max_keys) {
538 ret = ENOMEM;
539 } else {
540 *key = s_keys.next++;
541 s_keys.destructors[*key] = destructor;
542 ret = 0;
543 }
544 pthread_mutex_unlock(&s_keys.mutex);
545 return ret;
546}
547
548void* pthread_getspecific(pthread_key_t key)
549{
550 if (key < 0)
551 return nullptr;
552 if (key >= max_keys)
553 return nullptr;
554 return t_specifics.values[key];
555}
556
557int pthread_setspecific(pthread_key_t key, const void* value)
558{
559 if (key < 0)
560 return EINVAL;
561 if (key >= max_keys)
562 return EINVAL;
563
564 t_specifics.values[key] = const_cast<void*>(value);
565 return 0;
566}
567int pthread_setname_np(pthread_t thread, const char* name)
568{
569 if (!name)
570 return EFAULT;
571 return syscall(SC_set_thread_name, thread, name, strlen(name));
572}
573
574int pthread_getname_np(pthread_t thread, char* buffer, size_t buffer_size)
575{
576 return syscall(SC_get_thread_name, thread, buffer, buffer_size);
577}
578
579} // extern "C"