mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
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:
parent
aae26a3a1e
commit
618aebdd8a
Notes:
sideshowbarker
2024-07-19 11:10:03 +09:00
Author: https://github.com/ADKaster Commit: https://github.com/SerenityOS/serenity/commit/618aebdd8ab Pull-request: https://github.com/SerenityOS/serenity/pull/795
9 changed files with 596 additions and 23 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -33,3 +33,5 @@
|
|||
#define MB_LEN_MAX 16
|
||||
|
||||
#define ARG_MAX 65536
|
||||
|
||||
#define PTHREAD_STACK_MIN 65536
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
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;
|
||||
int rc = create_thread(start_routine, argument_to_start_routine, stack + stack_size);
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
254
Userland/tt.cpp
254
Userland/tt.cpp
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue