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.
This commit is contained in:
Andrew Kaster 2019-11-17 20:08:10 -07:00 committed by Andreas Kling
parent aae26a3a1e
commit 618aebdd8a
Notes: sideshowbarker 2024-07-19 11:10:03 +09:00
9 changed files with 596 additions and 23 deletions

View file

@ -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<u32>(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<ThreadPriority>(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;

View file

@ -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);

View file

@ -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();

View file

@ -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 };

View file

@ -33,3 +33,5 @@
#define MB_LEN_MAX 16
#define ARG_MAX 65536
#define PTHREAD_STACK_MIN 65536

View file

@ -1,7 +1,7 @@
#pragma once
#include <stddef.h>
#include <bits/stdint.h>
#include <stddef.h>
#include <sys/cdefs.h>
__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

View file

@ -2,11 +2,22 @@
#include <AK/Atomic.h>
#include <AK/StdLibExtras.h>
#include <Kernel/Syscall.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#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<PthreadAttrImpl**>(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<PthreadAttrImpl**>(attributes));
delete attributes_impl;
return 0;
}
int pthread_attr_getdetachstate(const pthread_attr_t* attributes, int* p_detach_state)
{
auto* attributes_impl = *(reinterpret_cast<const PthreadAttrImpl* const*>(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<PthreadAttrImpl**>(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<const PthreadAttrImpl* const*>(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<PthreadAttrImpl**>(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<const PthreadAttrImpl* const*>(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<PthreadAttrImpl**>(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<const PthreadAttrImpl* const*>(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<PthreadAttrImpl**>(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<const PthreadAttrImpl* const*>(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<PthreadAttrImpl**>(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;
}
}

View file

@ -3,19 +3,10 @@
#include <sched.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
__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);

View file

@ -1,14 +1,28 @@
#include <mman.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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;
}