Serenity Operating System
at hosted 579 lines 17 kB view raw
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"