* Copyright 2008-2009, Axel DΓΆrfler, axeld@pinc-software.de.
* Copyright 2006, JΓ©rΓ΄me Duval. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "pthread_private.h"
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <TLS.h>
#include <syscalls.h>
#include <thread_defs.h>
#include <tls.h>
#define THREAD_DETACHED 0x01
#define THREAD_DEAD 0x02
static const pthread_attr pthread_attr_default = {
PTHREAD_CREATE_JOINABLE,
B_NORMAL_PRIORITY,
USER_STACK_SIZE
};
static pthread_thread sMainThread;
static int sConcurrencyLevel;
static status_t
pthread_thread_entry(thread_func _unused, void* _thread)
{
pthread_thread* thread = (pthread_thread*)_thread;
*tls_address(TLS_PTHREAD_SLOT) = thread;
pthread_exit(thread->entry(thread->entry_argument));
return 0;
}
void
__pthread_destroy_thread(void)
{
pthread_thread* thread = pthread_self();
while (true) {
struct __pthread_cleanup_handler* handler
= __pthread_cleanup_pop_handler();
if (handler == NULL)
break;
handler->function(handler->argument);
}
__pthread_key_call_destructors(thread);
if ((atomic_or(&thread->flags, THREAD_DEAD) & THREAD_DETACHED) != 0)
free(thread);
}
pthread_thread*
__allocate_pthread(void *data)
{
pthread_thread* thread = (pthread_thread*)malloc(sizeof(pthread_thread));
if (thread == NULL)
return NULL;
thread->entry = NULL;
thread->entry_argument = data;
thread->exit_value = NULL;
thread->cancel_state = PTHREAD_CANCEL_ENABLE;
thread->cancel_type = PTHREAD_CANCEL_DEFERRED;
thread->cancelled = false;
thread->cleanup_handlers = NULL;
thread->flags = 0;
memset(thread->specific, 0, sizeof(thread->specific));
return thread;
}
int
pthread_create(pthread_t* _thread, const pthread_attr_t* _attr,
void* (*startRoutine)(void*), void* arg)
{
const pthread_attr* attr = NULL;
pthread_thread* thread;
struct thread_creation_attributes attributes;
if (_thread == NULL)
return EINVAL;
if (_attr == NULL)
attr = &pthread_attr_default;
else {
attr = *_attr;
if (attr == NULL)
return EINVAL;
}
thread = __allocate_pthread(arg);
if (thread == NULL)
return EAGAIN;
thread->entry = startRoutine;
if (attr->detach_state == PTHREAD_CREATE_DETACHED)
thread->flags |= THREAD_DETACHED;
attributes.entry = pthread_thread_entry;
attributes.name = "pthread func";
attributes.priority = attr->sched_priority;
attributes.args1 = NULL;
attributes.args2 = thread;
attributes.stack_address = NULL;
attributes.stack_size = attr->stack_size;
thread->id = _kern_spawn_thread(&attributes);
if (thread->id < 0) {
free(thread);
return EAGAIN;
}
resume_thread(thread->id);
*_thread = thread;
return 0;
}
pthread_t
pthread_self(void)
{
pthread_thread* thread;
thread = (pthread_thread*)tls_get(TLS_PTHREAD_SLOT);
if (thread == NULL)
return &sMainThread;
return thread;
}
int
pthread_equal(pthread_t t1, pthread_t t2)
{
return t1 != NULL && t2 != NULL && t1 == t2;
}
int
pthread_join(pthread_t thread, void** _value)
{
status_t dummy;
status_t error = wait_for_thread(thread->id, &dummy);
if (error == B_BAD_THREAD_ID)
return ESRCH;
if (_value != NULL)
*_value = thread->exit_value;
if ((atomic_or(&thread->flags, THREAD_DETACHED) & THREAD_DEAD) != 0)
free(thread);
return B_TO_POSIX_ERROR(error);
}
void
pthread_exit(void* value)
{
pthread_self()->exit_value = value;
exit_thread(B_OK);
}
int
pthread_kill(pthread_t thread, int sig)
{
status_t status = send_signal(thread->id, (uint)sig);
if (status != B_OK) {
if (status == B_BAD_THREAD_ID)
return ESRCH;
return B_TO_POSIX_ERROR(status);
}
return 0;
}
int
pthread_detach(pthread_t thread)
{
int32 flags;
if (thread == NULL)
return EINVAL;
flags = atomic_or(&thread->flags, THREAD_DETACHED);
if ((flags & THREAD_DETACHED) != 0)
return 0;
if ((flags & THREAD_DEAD) != 0)
free(thread);
return 0;
}
int
pthread_getconcurrency(void)
{
return sConcurrencyLevel;
}
int
pthread_setconcurrency(int newLevel)
{
if (newLevel < 0)
return EINVAL;
sConcurrencyLevel = newLevel;
return 0;
}
int
pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
{
thread_info info;
status_t status = _kern_get_thread_info(thread->id, &info);
if (status == B_BAD_THREAD_ID)
return ESRCH;
param->sched_priority = info.priority;
if (policy != NULL)
*policy = SCHED_RR;
return 0;
}
int
pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param *param)
{
status_t status;
if (policy != SCHED_RR)
return ENOTSUP;
status = _kern_set_thread_priority(thread->id, param->sched_priority);
if (status == B_BAD_THREAD_ID)
return ESRCH;
return status;
}
thread_id
get_pthread_thread_id(pthread_t thread)
{
return thread->id;
}