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