Serenity Operating System
at portability 578 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 <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"