From 618aebdd8ab55f9b63256e8195fa4e0f6fe305aa Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Sun, 17 Nov 2019 20:08:10 -0700 Subject: [PATCH] Kernel+LibPthread: pthread_create handles pthread_attr_t Add an initial implementation of pthread attributes for: * detach state (joinable, detached) * schedule params (just priority) * guard page size (as skeleton) (requires kernel support maybe?) * stack size and user-provided stack location (4 or 8 MB only, must be aligned) Add some tests too, to the thread test program. Also, LibC: Move pthread declarations to sys/types.h, where they belong. --- Kernel/Process.cpp | 33 +++- Kernel/Process.h | 2 +- Kernel/Syscall.h | 14 ++ Kernel/Thread.h | 4 + Libraries/LibC/limits.h | 2 + Libraries/LibC/sys/types.h | 14 +- Libraries/LibPthread/pthread.cpp | 266 ++++++++++++++++++++++++++++++- Libraries/LibPthread/pthread.h | 30 ++-- Userland/tt.cpp | 254 ++++++++++++++++++++++++++++- 9 files changed, 596 insertions(+), 23 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 2abac19167..2fe517437a 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2851,18 +2851,42 @@ int Process::thread_count() const return count; } -int Process::sys$create_thread(void* (*entry)(void*), void* argument, void* stack) +int Process::sys$create_thread(void* (*entry)(void*), void* argument, const Syscall::SC_create_thread_params* params) { if (!validate_read((const void*)entry, sizeof(void*))) return -EFAULT; - if (!MM.validate_user_stack(*this, VirtualAddress(((u32)stack) - 4))) + + if (!validate_read_typed(params)) return -EFAULT; + + u32 user_stack_address = reinterpret_cast(params->m_stack_location) + params->m_stack_size; + + if (!MM.validate_user_stack(*this, VirtualAddress(user_stack_address - 4))) + return -EFAULT; + + // FIXME: return EAGAIN if Thread::all_threads().size() is greater than PTHREAD_THREADS_MAX + + ThreadPriority requested_thread_priority = static_cast(params->m_schedule_priority); + if (requested_thread_priority < ThreadPriority::First || requested_thread_priority > ThreadPriority::Last) + return -EINVAL; + + if (requested_thread_priority != ThreadPriority::Normal && !is_superuser()) + return -EPERM; + + bool is_thread_joinable = (0 == params->m_detach_state); + + // FIXME: Do something with guard pages? + auto* thread = new Thread(*this); + + thread->set_priority(requested_thread_priority); + thread->set_joinable(is_thread_joinable); + auto& tss = thread->tss(); tss.eip = (u32)entry; tss.eflags = 0x0202; tss.cr3 = page_directory().cr3(); - tss.esp = (u32)stack; + tss.esp = user_stack_address; // NOTE: The stack needs to be 16-byte aligned. thread->push_value_on_stack((u32)argument); @@ -2915,7 +2939,8 @@ int Process::sys$join_thread(int tid, void** exit_value) if (thread->m_joiner) return -EINVAL; - // FIXME: EINVAL: 'thread' is not a joinable thread + if (!thread->is_joinable()) + return -EINVAL; void* joinee_exit_value = nullptr; diff --git a/Kernel/Process.h b/Kernel/Process.h index 035c48cee2..389c529ef6 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -203,7 +203,7 @@ public: int sys$sched_setparam(pid_t pid, const struct sched_param* param); int sys$sched_getparam(pid_t pid, struct sched_param* param); int sys$restore_signal_mask(u32 mask); - int sys$create_thread(void* (*)(void*), void* argument, void* stack); + int sys$create_thread(void* (*)(void*), void* argument, const Syscall::SC_create_thread_params*); void sys$exit_thread(void*); int sys$join_thread(int tid, void** exit_value); int sys$rename(const char* oldpath, const char* newpath); diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 6308dcdf94..530c1c9b51 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -248,6 +248,20 @@ struct SC_setsockopt_params { socklen_t value_size; }; +struct SC_create_thread_params { + unsigned int m_detach_state = 0; // JOINABLE or DETACHED + int m_schedule_priority = 2; // ThreadPriority::Normal + // FIXME: Implment guard pages in create_thread (unreadable pages at "overflow" end of stack) + // "If an implementation rounds up the value of guardsize to a multiple of {PAGESIZE}, + // a call to pthread_attr_getguardsize() specifying attr shall store in the guardsize + // parameter the guard size specified by the previous pthread_attr_setguardsize() function call" + // ... ok, if you say so posix. Guess we get to lie to people about guard page size + unsigned int m_guard_page_size = 0; // Rounded up to PAGE_SIZE + unsigned int m_reported_guard_page_size = 0; // The lie we tell callers + unsigned int m_stack_size = 4 * MB; // Default PTHREAD_STACK_MIN + void* m_stack_location = nullptr; // nullptr means any, o.w. process virtual address +}; + void initialize(); int sync(); diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 7017df3dd9..2b53b9b50f 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -63,6 +63,9 @@ public: void set_priority(ThreadPriority p) { m_priority = p; } ThreadPriority priority() const { return m_priority; } + void set_joinable(bool j) { m_is_joinable = j; } + bool is_joinable() const { return m_is_joinable; } + Process& process() { return m_process; } const Process& process() const { return m_process; } @@ -368,6 +371,7 @@ private: SignalActionData m_signal_action_data[32]; Blocker* m_blocker { nullptr }; + bool m_is_joinable { true }; Thread* m_joiner { nullptr }; Thread* m_joinee { nullptr }; void* m_exit_value { nullptr }; diff --git a/Libraries/LibC/limits.h b/Libraries/LibC/limits.h index 1c5f583556..d89f7228ee 100644 --- a/Libraries/LibC/limits.h +++ b/Libraries/LibC/limits.h @@ -33,3 +33,5 @@ #define MB_LEN_MAX 16 #define ARG_MAX 65536 + +#define PTHREAD_STACK_MIN 65536 diff --git a/Libraries/LibC/sys/types.h b/Libraries/LibC/sys/types.h index cda19da382..9f7f3edab2 100644 --- a/Libraries/LibC/sys/types.h +++ b/Libraries/LibC/sys/types.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include __BEGIN_DECLS @@ -60,4 +60,16 @@ struct utimbuf { time_t modtime; }; +typedef int pthread_t; +typedef void* pthread_key_t; +typedef void* pthread_once_t; +typedef uint32_t pthread_mutex_t; +typedef void* pthread_attr_t; +typedef void* pthread_mutexattr_t; +typedef void* pthread_cond_t; +typedef void* pthread_rwlock_t; +typedef void* pthread_rwlockatrr_t; +typedef void* pthread_spinlock_t; +typedef void* pthread_condattr_t; + __END_DECLS diff --git a/Libraries/LibPthread/pthread.cpp b/Libraries/LibPthread/pthread.cpp index ba844ce400..2057a25554 100644 --- a/Libraries/LibPthread/pthread.cpp +++ b/Libraries/LibPthread/pthread.cpp @@ -2,11 +2,22 @@ #include #include #include +#include #include #include #include #include +#define PTHREAD_DEBUG + +namespace { +using PthreadAttrImpl = Syscall::SC_create_thread_params; +} // end anonymous namespace + +constexpr size_t required_stack_alignment = 4 * MB; +constexpr size_t highest_reasonable_guard_size = 32 * PAGE_SIZE; +constexpr size_t highest_reasonable_stack_size = 8 * MB; // That's the default in Ubuntu? + extern "C" { static int create_thread(void* (*entry)(void*), void* argument, void* stack) @@ -25,12 +36,33 @@ int pthread_create(pthread_t* thread, pthread_attr_t* attributes, void* (*start_ { if (!thread) return -EINVAL; - UNUSED_PARAM(attributes); - const size_t stack_size = 4 * MB; - auto* stack = (u8*)mmap_with_name(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Thread stack"); - if (!stack) - return -1; - int rc = create_thread(start_routine, argument_to_start_routine, stack + stack_size); + + PthreadAttrImpl default_attributes {}; + PthreadAttrImpl** arg_attributes = reinterpret_cast(attributes); + + PthreadAttrImpl* used_attributes = arg_attributes ? *arg_attributes : &default_attributes; + + if (!used_attributes->m_stack_location) { + // adjust stack size, user might have called setstacksize, which has no restrictions on size/alignment + if (0 != (used_attributes->m_stack_size % required_stack_alignment)) + used_attributes->m_stack_size += required_stack_alignment - (used_attributes->m_stack_size % required_stack_alignment); + + 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"); + if (!used_attributes->m_stack_location) + return -1; + } + +#ifdef PTHREAD_DEBUG + printf("pthread_create: Creating thread with attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + used_attributes, + (PTHREAD_CREATE_JOINABLE == used_attributes->m_detach_state) ? "joinable" : "detached", + used_attributes->m_schedule_priority, + used_attributes->m_guard_page_size, + used_attributes->m_stack_size, + used_attributes->m_stack_location); +#endif + + int rc = create_thread(start_routine, argument_to_start_routine, used_attributes); if (rc < 0) return rc; *thread = rc; @@ -73,4 +105,226 @@ int pthread_mutex_unlock(pthread_mutex_t* mutex) atomic->store(false, AK::memory_order_release); return 0; } + +int pthread_attr_init(pthread_attr_t* attributes) +{ + auto* impl = new PthreadAttrImpl {}; + *attributes = impl; + +#ifdef PTHREAD_DEBUG + printf("pthread_attr_init: New thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + impl, + (PTHREAD_CREATE_JOINABLE == impl->m_detach_state) ? "joinable" : "detached", + impl->m_schedule_priority, + impl->m_guard_page_size, + impl->m_stack_size, + impl->m_stack_location); +#endif + + return 0; +} + +int pthread_attr_destroy(pthread_attr_t* attributes) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + delete attributes_impl; + return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t* attributes, int* p_detach_state) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl || !p_detach_state) + return EINVAL; + + *p_detach_state = attributes_impl->m_detach_state; + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t* attributes, int detach_state) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl) + return EINVAL; + + if ((PTHREAD_CREATE_JOINABLE != detach_state) || PTHREAD_CREATE_DETACHED != detach_state) + return EINVAL; + + attributes_impl->m_detach_state = detach_state; + +#ifdef PTHREAD_DEBUG + printf("pthread_attr_setdetachstate: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + attributes_impl, + (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached", + attributes_impl->m_schedule_priority, + attributes_impl->m_guard_page_size, + attributes_impl->m_stack_size, + attributes_impl->m_stack_location); +#endif + + return 0; +} + +int pthread_attr_getguardsize(const pthread_attr_t* attributes, size_t* p_guard_size) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl || !p_guard_size) + return EINVAL; + + *p_guard_size = attributes_impl->m_reported_guard_page_size; + return 0; +} + +int pthread_attr_setguardsize(pthread_attr_t* attributes, size_t guard_size) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl) + return EINVAL; + + size_t actual_guard_size = guard_size; + // round up + if (0 != (guard_size % PAGE_SIZE)) + actual_guard_size += PAGE_SIZE - (guard_size % PAGE_SIZE); + + // what is the user even doing? + if (actual_guard_size > highest_reasonable_guard_size) { + return EINVAL; + } + + attributes_impl->m_guard_page_size = actual_guard_size; + attributes_impl->m_reported_guard_page_size = guard_size; // POSIX, why? + +#ifdef PTHREAD_DEBUG + printf("pthread_attr_setguardsize: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + attributes_impl, + (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached", + attributes_impl->m_schedule_priority, + attributes_impl->m_guard_page_size, + attributes_impl->m_stack_size, + attributes_impl->m_stack_location); +#endif + + return 0; +} + +int pthread_attr_getschedparam(const pthread_attr_t* attributes, struct sched_param* p_sched_param) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl || !p_sched_param) + return EINVAL; + + p_sched_param->sched_priority = attributes_impl->m_schedule_priority; + return 0; +} + +int pthread_attr_setschedparam(pthread_attr_t* attributes, const struct sched_param* p_sched_param) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + if (!attributes_impl || !p_sched_param) + return EINVAL; + + // NOTE: This must track sched_get_priority_[min,max] and ThreadPriority enum in Thread.h + if (p_sched_param->sched_priority < 0 || p_sched_param->sched_priority > 3) + return ENOTSUP; + + attributes_impl->m_schedule_priority = p_sched_param->sched_priority; + +#ifdef PTHREAD_DEBUG + printf("pthread_attr_setschedparam: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + attributes_impl, + (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached", + attributes_impl->m_schedule_priority, + attributes_impl->m_guard_page_size, + attributes_impl->m_stack_size, + attributes_impl->m_stack_location); +#endif + + return 0; +} + +int pthread_attr_getstack(const pthread_attr_t* attributes, void** p_stack_ptr, size_t* p_stack_size) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl || !p_stack_ptr || !p_stack_size) + return EINVAL; + + *p_stack_ptr = attributes_impl->m_stack_location; + *p_stack_size = attributes_impl->m_stack_size; + + return 0; +} + +int pthread_attr_setstack(pthread_attr_t* attributes, void* p_stack, size_t stack_size) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl || !p_stack) + return EINVAL; + + // Check for required alignment on size + if (0 != (stack_size % required_stack_alignment)) + return EINVAL; + + // FIXME: Check for required alignment on pointer? + + // FIXME: "[EACCES] The stack page(s) described by stackaddr and stacksize are not both readable and writable by the thread." + // Have to check that the whole range is mapped to this process/thread? Can we defer this to create_thread? + + attributes_impl->m_stack_size = stack_size; + attributes_impl->m_stack_location = p_stack; + +#ifdef PTHREAD_DEBUG + printf("pthread_attr_setstack: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + attributes_impl, + (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached", + attributes_impl->m_schedule_priority, + attributes_impl->m_guard_page_size, + attributes_impl->m_stack_size, + attributes_impl->m_stack_location); +#endif + + return 0; +} + +int pthread_attr_getstacksize(const pthread_attr_t* attributes, size_t* p_stack_size) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl || !p_stack_size) + return EINVAL; + + *p_stack_size = attributes_impl->m_stack_size; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t* attributes, size_t stack_size) +{ + auto* attributes_impl = *(reinterpret_cast(attributes)); + + if (!attributes_impl) + return EINVAL; + + if ((stack_size < PTHREAD_STACK_MIN) || stack_size > highest_reasonable_stack_size) + return EINVAL; + + attributes_impl->m_stack_size = stack_size; + +#ifdef PTHREAD_DEBUG + printf("pthread_attr_setstacksize: Thread attributes at %p, detach state %s, priority %d, guard page size %d, stack size %d, stack location %p\n", + attributes_impl, + (PTHREAD_CREATE_JOINABLE == attributes_impl->m_detach_state) ? "joinable" : "detached", + attributes_impl->m_schedule_priority, + attributes_impl->m_guard_page_size, + attributes_impl->m_stack_size, + attributes_impl->m_stack_location); +#endif + + return 0; +} } diff --git a/Libraries/LibPthread/pthread.h b/Libraries/LibPthread/pthread.h index 5320a88539..f5e02767f8 100644 --- a/Libraries/LibPthread/pthread.h +++ b/Libraries/LibPthread/pthread.h @@ -3,19 +3,10 @@ #include #include #include +#include __BEGIN_DECLS -typedef int pthread_t; -typedef void* pthread_key_t; -typedef void* pthread_once_t; -typedef uint32_t pthread_mutex_t; -typedef void* pthread_attr_t; -typedef void* pthread_mutexattr_t; -typedef void* pthread_cond_t; -typedef void* pthread_spinlock_t; -typedef void* pthread_condattr_t; - int pthread_create(pthread_t*, pthread_attr_t*, void* (*)(void*), void*); void pthread_exit(void*); int pthread_kill(pthread_t, int); @@ -27,9 +18,28 @@ int pthread_mutex_trylock(pthread_mutex_t* mutex); int pthread_mutex_unlock(pthread_mutex_t*); int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*); int pthread_mutex_destroy(pthread_mutex_t*); + int pthread_attr_init(pthread_attr_t*); int pthread_attr_destroy(pthread_attr_t*); +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 1 + +int pthread_attr_getdetachstate(const pthread_attr_t*, int*); +int pthread_attr_setdetachstate(pthread_attr_t*, int); + +int pthread_attr_getguardsize(const pthread_attr_t*, size_t*); +int pthread_attr_setguardsize(pthread_attr_t*, size_t); + +int pthread_attr_getschedparam(const pthread_attr_t*, struct sched_param*); +int pthread_attr_setschedparam(pthread_attr_t*, const struct sched_param*); + +int pthread_attr_getstack(const pthread_attr_t*, void**, size_t*); +int pthread_attr_setstack(pthread_attr_t* attr, void*, size_t); + +int pthread_attr_getstacksize(const pthread_attr_t*, size_t*); +int pthread_attr_setstacksize(pthread_attr_t*, size_t); + int pthread_once(pthread_once_t*, void (*)(void)); #define PTHREAD_ONCE_INIT 0 void* pthread_getspecific(pthread_key_t key); diff --git a/Userland/tt.cpp b/Userland/tt.cpp index 2823a19103..ea966ce8cb 100644 --- a/Userland/tt.cpp +++ b/Userland/tt.cpp @@ -1,14 +1,28 @@ +#include #include #include -#include #include +#include +#include static int mutex_test(); +static int detached_test(); +static int priority_test(); +static int stack_size_test(); +static int set_stack_test(); int main(int argc, char** argv) { if (argc == 2 && *argv[1] == 'm') return mutex_test(); + if (argc == 2 && *argv[1] == 'd') + return detached_test(); + if (argc == 2 && *argv[1] == 'p') + return priority_test(); + if (argc == 2 && *argv[1] == 's') + return stack_size_test(); + if (argc == 2 && *argv[1] == 'x') + return set_stack_test(); printf("Hello from the first thread!\n"); pthread_t thread_id; @@ -70,3 +84,241 @@ int mutex_test() } return 0; } + +int detached_test() +{ + pthread_attr_t attributes; + int rc = pthread_attr_init(&attributes); + if (rc != 0) { + printf("pthread_attr_setdetachstate: %s\n", strerror(rc)); + return 1; + } + + int detach_state = 99; // clearly invalid + rc = pthread_attr_getdetachstate(&attributes, &detach_state); + if (rc != 0) { + printf("pthread_attr_setdetachstate: %s\n", strerror(rc)); + return 2; + } + printf("Default detach state: %s\n", detach_state == PTHREAD_CREATE_JOINABLE ? "joinable" : "detached"); + + detach_state = PTHREAD_CREATE_DETACHED; + rc = pthread_attr_setdetachstate(&attributes, detach_state); + if (rc != 0) { + printf("pthread_attr_setdetachstate: %s\n", strerror(rc)); + return 3; + } + printf("Set detach state on new thread to detached\n"); + + pthread_t thread_id; + rc = pthread_create( + &thread_id, &attributes, [](void*) -> void* { + printf("I'm the secondary thread :^)\n"); + sleep(1); + pthread_exit((void*)0xDEADBEEF); + return nullptr; + }, + nullptr); + if (rc < 0) { + perror("pthread_create"); + return 4; + } + + void* ret_val; + errno = 0; + rc = pthread_join(thread_id, &ret_val); + if (rc < 0 && errno != EINVAL) { + perror("pthread_join"); + return 5; + } + if (errno != EINVAL) { + printf("Expected EINVAL! Thread was joinable?\n"); + return 6; + } + + sleep(2); + printf("Thread was created detached. I sure hope it exited on its own.\n"); + + rc = pthread_attr_destroy(&attributes); + if (rc != 0) { + printf("pthread_attr_setdetachstate: %s\n", strerror(rc)); + return 7; + } + + return 0; +} + +int priority_test() +{ + pthread_attr_t attributes; + int rc = pthread_attr_init(&attributes); + if (rc != 0) { + printf("pthread_attr_init: %s\n", strerror(rc)); + return 1; + } + + struct sched_param sched_params; + rc = pthread_attr_getschedparam(&attributes, &sched_params); + if (rc != 0) { + printf("pthread_attr_getschedparam: %s\n", strerror(rc)); + return 2; + } + printf("Default priority: %d\n", sched_params.sched_priority); + + sched_params.sched_priority = 3; + rc = pthread_attr_setschedparam(&attributes, &sched_params); + if (rc != 0) { + printf("pthread_attr_setschedparam: %s\n", strerror(rc)); + return 3; + } + printf("Set thread priority to 3\n"); + + pthread_t thread_id; + rc = pthread_create( + &thread_id, &attributes, [](void*) -> void* { + printf("I'm the secondary thread :^)\n"); + sleep(1); + pthread_exit((void*)0xDEADBEEF); + return nullptr; + }, + nullptr); + if (rc < 0) { + perror("pthread_create"); + return 4; + } + + rc = pthread_join(thread_id, nullptr); + if (rc < 0) { + perror("pthread_join"); + return 5; + } + + rc = pthread_attr_destroy(&attributes); + if (rc != 0) { + printf("pthread_attr_destroy: %s\n", strerror(rc)); + return 6; + } + + return 0; +} + +int stack_size_test() +{ + pthread_attr_t attributes; + int rc = pthread_attr_init(&attributes); + if (rc != 0) { + printf("pthread_attr_init: %s\n", strerror(rc)); + return 1; + } + + size_t stack_size; + rc = pthread_attr_getstacksize(&attributes, &stack_size); + if (rc != 0) { + printf("pthread_attr_getstacksize: %s\n", strerror(rc)); + return 2; + } + printf("Default stack size: %zu\n", stack_size); + + stack_size = 8 * 1024 * 1024; + rc = pthread_attr_setstacksize(&attributes, stack_size); + if (rc != 0) { + printf("pthread_attr_setstacksize: %s\n", strerror(rc)); + return 3; + } + printf("Set thread stack size to 8 MB\n"); + + pthread_t thread_id; + rc = pthread_create( + &thread_id, &attributes, [](void*) -> void* { + printf("I'm the secondary thread :^)\n"); + sleep(1); + pthread_exit((void*)0xDEADBEEF); + return nullptr; + }, + nullptr); + if (rc < 0) { + perror("pthread_create"); + return 4; + } + + rc = pthread_join(thread_id, nullptr); + if (rc < 0) { + perror("pthread_join"); + return 5; + } + + rc = pthread_attr_destroy(&attributes); + if (rc != 0) { + printf("pthread_attr_destroy: %s\n", strerror(rc)); + return 6; + } + + return 0; +} + +int set_stack_test() +{ + pthread_attr_t attributes; + int rc = pthread_attr_init(&attributes); + if (rc < 0) { + printf("pthread_attr_init: %s\n", strerror(rc)); + return 1; + } + + size_t stack_size = 8 * 1024 * 1024; + void* stack_addr = mmap_with_name(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Cool stack"); + + if (!stack_addr) { + perror("mmap_with_name"); + return -1; + } + + rc = pthread_attr_setstack(&attributes, stack_addr, stack_size); + if (rc != 0) { + printf("pthread_attr_setstack: %s\n", strerror(rc)); + return 2; + } + printf("Set thread stack to %p, size %zu\n", stack_addr, stack_size); + + size_t stack_size_verify; + void* stack_addr_verify; + + rc = pthread_attr_getstack(&attributes, &stack_addr_verify, &stack_size_verify); + if (rc != 0) { + printf("pthread_attr_getstack: %s\n", strerror(rc)); + return 3; + } + + if (stack_addr != stack_addr_verify || stack_size != stack_size_verify) { + 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); + return 4; + } + + pthread_t thread_id; + rc = pthread_create( + &thread_id, &attributes, [](void*) -> void* { + printf("I'm the secondary thread :^)\n"); + sleep(1); + pthread_exit((void*)0xDEADBEEF); + return nullptr; + }, + nullptr); + if (rc < 0) { + perror("pthread_create"); + return 5; + } + + rc = pthread_join(thread_id, nullptr); + if (rc < 0) { + perror("pthread_join"); + return 6; + } + + rc = pthread_attr_destroy(&attributes); + if (rc != 0) { + printf("pthread_attr_destroy: %s\n", strerror(rc)); + return 7; + } + + return 0; +}