From e865022a31bd4848071557e2ad6715a8e5825bb7 Mon Sep 17 00:00:00 2001 From: Christian Mehlis Date: Thu, 13 Feb 2014 14:18:30 +0100 Subject: [PATCH] pthread: initial add --- sys/Makefile | 3 + sys/posix/pthread/Makefile | 7 + sys/posix/pthread/include/pthread.h | 99 +++++++ sys/posix/pthread/include/pthread_attr.h | 67 +++++ sys/posix/pthread/include/pthread_barrier.h | 25 ++ sys/posix/pthread/include/pthread_cond.h | 51 ++++ sys/posix/pthread/include/pthread_mutex.h | 81 ++++++ sys/posix/pthread/include/pthread_rwlock.h | 53 ++++ sys/posix/pthread/include/pthread_spin.h | 17 ++ sys/posix/pthread/include/pthreadtypes.h | 36 +++ sys/posix/pthread/pthread.c | 282 ++++++++++++++++++++ sys/posix/pthread/pthread_attr.c | 157 +++++++++++ sys/posix/pthread/pthread_mutex.c | 59 ++++ sys/posix/pthread/pthread_once.c | 30 +++ sys/posix/pthread/pthread_spin.c | 55 ++++ tests/test_pthread/Makefile | 22 ++ tests/test_pthread/main.c | 39 +++ 17 files changed, 1083 insertions(+) create mode 100644 sys/posix/pthread/Makefile create mode 100644 sys/posix/pthread/include/pthread.h create mode 100644 sys/posix/pthread/include/pthread_attr.h create mode 100644 sys/posix/pthread/include/pthread_barrier.h create mode 100644 sys/posix/pthread/include/pthread_cond.h create mode 100644 sys/posix/pthread/include/pthread_mutex.h create mode 100644 sys/posix/pthread/include/pthread_rwlock.h create mode 100644 sys/posix/pthread/include/pthread_spin.h create mode 100644 sys/posix/pthread/include/pthreadtypes.h create mode 100644 sys/posix/pthread/pthread.c create mode 100644 sys/posix/pthread/pthread_attr.c create mode 100644 sys/posix/pthread/pthread_mutex.c create mode 100644 sys/posix/pthread/pthread_once.c create mode 100644 sys/posix/pthread/pthread_spin.c create mode 100644 tests/test_pthread/Makefile create mode 100644 tests/test_pthread/main.c diff --git a/sys/Makefile b/sys/Makefile index 2a7ed4aa89..e6c9cfef07 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -21,6 +21,9 @@ endif ifneq (,$(filter pnet,$(USEMODULE))) DIRS += posix/pnet endif +ifneq (,$(filter pthread,$(USEMODULE))) + DIRS += posix/pthread +endif ifneq (,$(filter shell,$(USEMODULE))) DIRS += shell endif diff --git a/sys/posix/pthread/Makefile b/sys/posix/pthread/Makefile new file mode 100644 index 0000000000..96b84f147b --- /dev/null +++ b/sys/posix/pthread/Makefile @@ -0,0 +1,7 @@ +MODULE = pthread + +CFLAGS += -isystem $(RIOTBASE)/sys/posix/pthread/include + +export INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include + +include $(RIOTBASE)/Makefile.base diff --git a/sys/posix/pthread/include/pthread.h b/sys/posix/pthread/include/pthread.h new file mode 100644 index 0000000000..899f2ee5e4 --- /dev/null +++ b/sys/posix/pthread/include/pthread.h @@ -0,0 +1,99 @@ +#ifndef RIOT_PTHREAD_H +#define RIOT_PTHREAD_H 1 + +#include + +#include "kernel.h" +#include "mutex.h" +#include "sched.h" + +#include "pthreadtypes.h" + +/* Create a new thread, starting with execution of START-ROUTINE + getting passed ARG. Creation attributed come from ATTR. The new + handle is stored in *NEWTHREAD. */ +int pthread_create(pthread_t *newthread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + +/* Terminate calling thread. + + The registered cleanup handlers are called via exception handling .*/ +void pthread_exit(void *retval); + +/* Make calling thread wait for termination of the thread TH. The + exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN + is not NULL. + + This function is a cancellation point and therefore not marked with + . */ +int pthread_join(pthread_t th, void **thread_return); + +/* Indicate that the thread TH is never to be joined with PTHREAD_JOIN. + The resources of TH will therefore be freed immediately when it + terminates, instead of waiting for another thread to perform PTHREAD_JOIN + on it. */ +int pthread_detach(pthread_t th); + +/* Obtain the identifier of the current thread. */ +pthread_t pthread_self(void); + +/* Compare two thread identifiers. */ +int pthread_equal(pthread_t thread1, pthread_t thread2); + +#include "pthread_attr.h" + +/* Functions for scheduling control. */ + +/* Set the scheduling parameters for TARGET_THREAD according to POLICY + and *PARAM. */ +int pthread_setschedparam(pthread_t target_thread, int policy, + const struct sched_param *param); + +/* Return in *POLICY and *PARAM the scheduling parameters for TARGET_THREAD. */ +int pthread_getschedparam(pthread_t target_thread, int *policy, + struct sched_param *param); + +/* Set the scheduling priority for TARGET_THREAD. */ +int pthread_setschedprio(pthread_t target_thread, int prio); + +/* Functions for handling initialization. */ + +/* Guarantee that the initialization function INIT_ROUTINE will be called + only once, even if pthread_once is executed several times with the + same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or + variable initialized to PTHREAD_ONCE_INIT. + + The initialization functions might throw exception */ +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); + +/* Functions for handling cancellation. + + Note that these functions are explicitly not marked to not throw an + exception in C++ code. If cancellation is implemented by unwinding + this is necessary to have the compiler generate the unwind information. */ + +/* Set cancelability state of current thread to STATE, returning old + state in *OLDSTATE if OLDSTATE is not NULL. */ +int pthread_setcancelstate(int state, int *oldstate); + +/* Set cancellation state of current thread to TYPE, returning the old + type in *OLDTYPE if OLDTYPE is not NULL. */ +int pthread_setcanceltype(int type, int *oldtype); + +/* Cancel THREAD immediately or at the next possibility. */ +int pthread_cancel(pthread_t th); + +/* Test for pending cancellation for the current thread and terminate + the thread as per pthread_exit(PTHREAD_CANCELED) if it has been + cancelled. */ +void pthread_testcancel(void); + +#include "pthread_mutex.h" + +#include "pthread_rwlock.h" + +#include "pthread_spin.h" + +#include "pthread_barrier.h" + +#endif /* pthread.h */ diff --git a/sys/posix/pthread/include/pthread_attr.h b/sys/posix/pthread/include/pthread_attr.h new file mode 100644 index 0000000000..85b897e26e --- /dev/null +++ b/sys/posix/pthread/include/pthread_attr.h @@ -0,0 +1,67 @@ +/* Thread attribute handling. */ + +#define PTHREAD_CREATE_DETACHED (1) +#define PTHREAD_CREATE_JOINABLE (0) + +/* Initialize thread attribute *ATTR with default attributes + (detachstate is PTHREAD_JOINABLE, scheduling policy is SCHED_OTHER, + no user-provided stack). */ +int pthread_attr_init(pthread_attr_t *attr); + +/* Destroy thread attribute *ATTR. */ +int pthread_attr_destroy(pthread_attr_t *attr); + +/* Get detach state attribute. */ +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); + +/* Set detach state attribute. */ +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); + +/* Get the size of the guard area created for stack overflow protection. */ +int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize); + +/* Set the size of the guard area created for stack overflow protection. */ +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); + +/* Return in *PARAM the scheduling parameters of *ATTR. */ +int pthread_attr_getschedparam(const pthread_attr_t *attr, + struct sched_param *param); + +/* Set scheduling parameters (priority, etc) in *ATTR according to PARAM. */ +int pthread_attr_setschedparam(pthread_attr_t *attr, + const struct sched_param *param); + +/* Return in *POLICY the scheduling policy of *ATTR. */ +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); + +/* Set scheduling policy in *ATTR according to POLICY. */ +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); + +/* Return in *INHERIT the scheduling inheritance mode of *ATTR. */ +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit); + +/* Set scheduling inheritance mode in *ATTR according to INHERIT. */ +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit); + +/* Return in *SCOPE the scheduling contention scope of *ATTR. */ +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope); + +/* Set scheduling contention scope in *ATTR according to SCOPE. */ +int pthread_attr_setscope(pthread_attr_t *attr, int scope); + +/* Return the previously set address for the stack. */ +int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr); + +/* Set the starting address of the stack of the thread to be created. + Depending on whether the stack grows up or down the value must either + be higher or lower than all the address in the memory block. The + minimal size of the block must be PTHREAD_STACK_MIN. */ +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); + +/* Return the currently used minimal stack size. */ +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); + +/* Add information about the minimum stack size needed for the thread + to be started. This size must never be less than PTHREAD_STACK_MIN + and must also not exceed the system limits. */ +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); diff --git a/sys/posix/pthread/include/pthread_barrier.h b/sys/posix/pthread/include/pthread_barrier.h new file mode 100644 index 0000000000..f452405e1a --- /dev/null +++ b/sys/posix/pthread/include/pthread_barrier.h @@ -0,0 +1,25 @@ +/* Functions to handle barriers. */ + +/* Initialize BARRIER with the attributes in ATTR. The barrier is + opened when COUNT waiters arrived. */ +int pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, unsigned int count); + +/* Destroy a previously dynamically initialized barrier BARRIER. */ +int pthread_barrier_destroy(pthread_barrier_t *barrier); + +/* Wait on barrier BARRIER. */ +int pthread_barrier_wait(pthread_barrier_t *barrier); + +/* Initialize barrier attribute ATTR. */ +int pthread_barrierattr_init(pthread_barrierattr_t *attr); + +/* Destroy previously dynamically initialized barrier attribute ATTR. */ +int pthread_barrierattr_destroy(pthread_barrierattr_t *attr); + +/* Get the process-shared flag of the barrier attribute ATTR. */ +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *pshared); + +/* Set the process-shared flag of the barrier attribute ATTR. */ +int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared); diff --git a/sys/posix/pthread/include/pthread_cond.h b/sys/posix/pthread/include/pthread_cond.h new file mode 100644 index 0000000000..d6323509ae --- /dev/null +++ b/sys/posix/pthread/include/pthread_cond.h @@ -0,0 +1,51 @@ +/* Functions for handling conditional variables. */ + +/* Initialize condition variable COND using attributes ATTR, or use + the default values if later is NULL. */ +int pthread_cond_init(pthread_cond_t *cond, + const pthread_condattr_t *cond_attr); + +/* Destroy condition variable COND. */ +int pthread_cond_destroy(pthread_cond_t *cond); + +/* Wake up one thread waiting for condition variable COND. */ +int pthread_cond_signal(pthread_cond_t *cond); + +/* Wake up all threads waiting for condition variables COND. */ +int pthread_cond_broadcast(pthread_cond_t *cond); + +/* Wait for condition variable COND to be signaled or broadcast. + MUTEX is assumed to be locked before. + + This function is a cancellation point and therefore not marked with. */ +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); + +/* Wait for condition variable COND to be signaled or broadcast until + ABSTIME. MUTEX is assumed to be locked before. ABSTIME is an + absolute time specification; zero is the beginning of the epoch + (00:00:00 GMT, January 1, 1970). + + This function is a cancellation point and therefore not marked with. */ +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime); + +/* Functions for handling condition variable attributes. */ + +/* Initialize condition variable attribute ATTR. */ +int pthread_condattr_init(pthread_condattr_t *attr); + +/* Destroy condition variable attribute ATTR. */ +int pthread_condattr_destroy(pthread_condattr_t *attr); + +/* Get the process-shared flag of the condition variable attribute ATTR. */ +int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared); + +/* Set the process-shared flag of the condition variable attribute ATTR. */ +int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared); + +/* Get the clock selected for the conditon variable attribute ATTR. */ +int pthread_condattr_getclock(const pthread_condattr_t *attr, + clockid_t *clock_id); + +/* Set the clock selected for the conditon variable attribute ATTR. */ +int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id); diff --git a/sys/posix/pthread/include/pthread_mutex.h b/sys/posix/pthread/include/pthread_mutex.h new file mode 100644 index 0000000000..92ee0c2433 --- /dev/null +++ b/sys/posix/pthread/include/pthread_mutex.h @@ -0,0 +1,81 @@ +/* Mutex handling. */ + +#include + +#include "kernel.h" +#include "mutex.h" + +/* Initialize a mutex. */ +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr); + +/* Destroy a mutex. */ +int pthread_mutex_destroy(pthread_mutex_t *mutex); + +/* Try locking a mutex. */ +int pthread_mutex_trylock(pthread_mutex_t *mutex); + +/* Lock a mutex. */ +int pthread_mutex_lock(pthread_mutex_t *mutex); + +/* Wait until lock becomes available, or specified time passes. */ +int pthread_mutex_timedlock(pthread_mutex_t *mutex, + const struct timespec *abstime); + +/* Unlock a mutex. */ +int pthread_mutex_unlock(pthread_mutex_t *mutex); + +/* Get the priority ceiling of MUTEX. */ +int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, + int *prioceiling); + +/* Set the priority ceiling of MUTEX to PRIOCEILING, return old + priority ceiling value in *OLD_CEILING. */ +int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, + int *old_ceiling); + +/* Functions for handling mutex attributes. */ + +/* Initialize mutex attribute object ATTR with default attributes + (kind is PTHREAD_MUTEX_TIMED_NP). */ +int pthread_mutexattr_init(pthread_mutexattr_t *attr); + +/* Destroy mutex attribute object ATTR. */ +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); + +/* Get the process-shared flag of the mutex attribute ATTR. */ +int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, + int *pshared); + +/* Set the process-shared flag of the mutex attribute ATTR. */ +int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); + +/* Return in *KIND the mutex kind attribute in *ATTR. */ +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind); + +/* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_NORMAL, + PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, or + PTHREAD_MUTEX_DEFAULT). */ +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind); + +/* Return in *PROTOCOL the mutex protocol attribute in *ATTR. */ +int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, + int *protocol); + +/* Set the mutex protocol attribute in *ATTR to PROTOCOL (either + PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT, or PTHREAD_PRIO_PROTECT). */ +int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); + +/* Return in *PRIOCEILING the mutex prioceiling attribute in *ATTR. */ +int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, + int *prioceiling); + +/* Set the mutex prioceiling attribute in *ATTR to PRIOCEILING. */ +int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling); + +/* Get the robustness flag of the mutex attribute ATTR. */ +int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, + int *robustness); + +/* Set the robustness flag of the mutex attribute ATTR. */ +int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robustness); diff --git a/sys/posix/pthread/include/pthread_rwlock.h b/sys/posix/pthread/include/pthread_rwlock.h new file mode 100644 index 0000000000..7a726eba13 --- /dev/null +++ b/sys/posix/pthread/include/pthread_rwlock.h @@ -0,0 +1,53 @@ +/* Functions for handling read-write locks. */ + +/* Initialize read-write lock RWLOCK using attributes ATTR, or use + the default values if later is NULL. */ +int pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr); + +/* Destroy read-write lock RWLOCK. */ +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); + +/* Acquire read lock for RWLOCK. */ +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); + +/* Try to acquire read lock for RWLOCK. */ +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); + +/* Try to acquire read lock for RWLOCK or return after specfied time. */ +int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, + const struct timespec *abstime); + +/* Acquire write lock for RWLOCK. */ +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); + +/* Try to acquire write lock for RWLOCK. */ +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); + +/* Try to acquire write lock for RWLOCK or return after specfied time. */ +int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, + const struct timespec *abstime); + +/* Unlock RWLOCK. */ +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); + +/* Functions for handling read-write lock attributes. */ + +/* Initialize attribute object ATTR with default values. */ +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); + +/* Destroy attribute object ATTR. */ +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); + +/* Return current setting of process-shared attribute of ATTR in PSHARED. */ +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, + int *pshared); + +/* Set process-shared attribute of ATTR to PSHARED. */ +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); + +/* Return current setting of reader/writer preference. */ +int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref); + +/* Set reader/write preference. */ +int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref); diff --git a/sys/posix/pthread/include/pthread_spin.h b/sys/posix/pthread/include/pthread_spin.h new file mode 100644 index 0000000000..17c13ad913 --- /dev/null +++ b/sys/posix/pthread/include/pthread_spin.h @@ -0,0 +1,17 @@ +/* Functions to handle spinlocks. */ + +/* Initialize the spinlock LOCK. If PSHARED is nonzero the spinlock can + be shared between different processes. */ +int pthread_spin_init(pthread_spinlock_t *lock, int pshared); + +/* Destroy the spinlock LOCK. */ +int pthread_spin_destroy(pthread_spinlock_t *lock); + +/* Wait until spinlock LOCK is retrieved. */ +int pthread_spin_lock(pthread_spinlock_t *lock); + +/* Try to lock spinlock LOCK. */ +int pthread_spin_trylock(pthread_spinlock_t *lock); + +/* Release spinlock LOCK. */ +int pthread_spin_unlock(pthread_spinlock_t *lock); diff --git a/sys/posix/pthread/include/pthreadtypes.h b/sys/posix/pthread/include/pthreadtypes.h new file mode 100644 index 0000000000..3fdf0c7ad8 --- /dev/null +++ b/sys/posix/pthread/include/pthreadtypes.h @@ -0,0 +1,36 @@ +#ifndef PTHREADTYPES_H_ +#define PTHREADTYPES_H_ + +typedef unsigned long int pthread_t; + +typedef struct pthread_attr +{ + uint8_t detached; + char *ss_sp; + size_t ss_size; +} pthread_attr_t; + +struct sched_param { + int todo; /* TODO */ +}; + +/* Once-only execution */ +typedef int pthread_once_t; +/* Single execution handling. */ +#define PTHREAD_ONCE_INIT 0 + +typedef unsigned long int pthread_barrier_t; +typedef unsigned long int pthread_barrierattr_t; + +typedef unsigned long int pthread_cond_t; +typedef unsigned long int pthread_condattr_t; + +typedef mutex_t pthread_mutex_t; +typedef unsigned long int pthread_mutexattr_t; + +typedef unsigned long int pthread_rwlock_t; +typedef unsigned long int pthread_rwlockattr_t; + +typedef volatile int pthread_spinlock_t; + +#endif /* PTHREADTYPES_H_ */ diff --git a/sys/posix/pthread/pthread.c b/sys/posix/pthread/pthread.c new file mode 100644 index 0000000000..71a55a94c0 --- /dev/null +++ b/sys/posix/pthread/pthread.c @@ -0,0 +1,282 @@ +/** + * POSIX implementation of threading. + * + * Copyright (C) 2013 Freie Universität Berlin + * + * This file subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup posix + * @{ + * @file pthread.c + * @brief Implementation of pthread. + * @author Christian Mehlis + * @author René Kijewski + * @} + */ + +#include +#include +#include +#include + +#include "cpu-conf.h" +#include "irq.h" +#include "kernel_internal.h" +#include "msg.h" +#include "mutex.h" +#include "queue.h" +#include "thread.h" +#include "sched.h" + +#include "pthread.h" + +#define ENABLE_DEBUG (1) + +#if ENABLE_DEBUG +#define PTHREAD_REAPER_STACKSIZE KERNEL_CONF_STACKSIZE_MAIN +#define PTHREAD_STACKSIZE KERNEL_CONF_STACKSIZE_MAIN +#else +#define PTHREAD_REAPER_STACKSIZE KERNEL_CONF_STACKSIZE_DEFAULT +#define PTHREAD_STACKSIZE KERNEL_CONF_STACKSIZE_DEFAULT +#endif + +#include "debug.h" + +enum pthread_thread_status { + PTS_RUNNING, + PTS_DETACHED, + PTS_ZOMBIE, +}; + +typedef struct pthread_thread { + enum pthread_thread_status status; + int joining_thread; + int thread_pid; + void *returnval; + bool should_cancel; + + void *(*start_routine)(void *); + void *arg; + + char *stack; +} pthread_thread_t; + +static pthread_thread_t *volatile pthread_sched_threads[MAXTHREADS]; +static struct mutex_t pthread_mutex; + +static volatile int pthread_reaper_pid = -1; + +char pthread_reaper_stack[PTHREAD_REAPER_STACKSIZE]; + +static void pthread_start_routine(void) +{ + pthread_t self = pthread_self(); + pthread_thread_t *pt = pthread_sched_threads[self]; + void *retval = pt->start_routine(pt->arg); + pthread_exit(retval); +} + +static int insert(pthread_thread_t *pt) +{ + int result = -1; + mutex_lock(&pthread_mutex); + for (int i = 0; i < MAXTHREADS; i++){ + if (!pthread_sched_threads[i]) { + pthread_sched_threads[i] = pt; + result = i; + break; + } + } + mutex_unlock(&pthread_mutex); + return result; +} + +static void pthread_reaper(void) +{ + while (1) { + msg_t m; + msg_receive(&m); + DEBUG("pthread_reaper(): free(%p)\n", m.content.ptr); + free(m.content.ptr); + } +} + +int pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ + pthread_thread_t *pt = calloc(1, sizeof(pthread_thread_t)); + + int pthread_pid = insert(pt); + if (pthread_pid < 0) { + free(pt); + return -1; + } + *newthread = pthread_pid; + + pt->status = attr && attr->detached ? PTS_DETACHED : PTS_RUNNING; + pt->start_routine = start_routine; + pt->arg = arg; + + bool autofree = attr == NULL || attr->ss_sp == NULL || attr->ss_size == 0; + size_t stack_size = attr && attr->ss_size > 0 ? attr->ss_size : PTHREAD_STACKSIZE; + void *stack = autofree ? malloc(stack_size) : attr->ss_sp; + pt->stack = autofree ? stack : NULL; + + if (autofree && pthread_reaper_pid < 0) { + mutex_lock(&pthread_mutex); + if (pthread_reaper_pid < 0) { + /* volatile pid to overcome problems with double checking */ + volatile int pid = thread_create(pthread_reaper_stack, + PTHREAD_REAPER_STACKSIZE, + 0, + CREATE_STACKTEST, + pthread_reaper, + "pthread-reaper"); + pthread_reaper_pid = pid; + } + mutex_unlock(&pthread_mutex); + } + + pt->thread_pid = thread_create(stack, + stack_size, + PRIORITY_MAIN, + CREATE_WOUT_YIELD | CREATE_STACKTEST, + pthread_start_routine, + "pthread"); + if (pt->thread_pid < 0) { + free(pt->stack); + free(pt); + pthread_sched_threads[pthread_pid] = NULL; + return -1; + } + + sched_switch(active_thread->priority, PRIORITY_MAIN, inISR()); + + return 0; +} + +void pthread_exit(void *retval) +{ + pthread_thread_t *self = pthread_sched_threads[pthread_self()]; + DEBUG("pthread_exit(%p), self == %p\n", retval, (void *) self); + if (self->status != PTS_DETACHED) { + self->returnval = retval; + self->status = PTS_ZOMBIE; + + if (self->joining_thread) { + /* our thread got an other thread waiting for us */ + thread_wakeup(self->joining_thread); + } + } + + dINT(); + if (self->stack) { + msg_t m; + m.content.ptr = self->stack; + msg_send_int(&m, pthread_reaper_pid); + } + sched_task_exit(); +} + +int pthread_join(pthread_t th, void **thread_return) +{ + pthread_thread_t *other = pthread_sched_threads[th]; + if (!other) { + return -1; + } + + switch (other->status) { + case (PTS_RUNNING): + other->joining_thread = thread_pid; + /* go blocked, I'm waking up if other thread exits */ + thread_sleep(); + /* no break */ + case (PTS_ZOMBIE): + if (thread_return) { + *thread_return = other->returnval; + } + free(other); + /* we only need to free the pthread layer struct, + native thread stack is freed by other */ + pthread_sched_threads[th] = NULL; + return 0; + case (PTS_DETACHED): + return -1; + } + + return -2; +} + +int pthread_detach(pthread_t th) +{ + pthread_thread_t *other = pthread_sched_threads[th]; + if (!other) { + return -1; + } + + if (other->status == PTS_ZOMBIE) { + free(other); + /* we only need to free the pthread layer struct, + native thread stack is freed by other */ + pthread_sched_threads[th] = NULL; + } else { + other->status = PTS_DETACHED; + } + + return 0; +} + +pthread_t pthread_self(void) +{ + pthread_t result = -1; + mutex_lock(&pthread_mutex); + int pid = thread_pid; /* thread_pid is volatile */ + for (int i = 0; i < MAXTHREADS; i++) { + if (pthread_sched_threads[i] && pthread_sched_threads[i]->thread_pid == pid) { + result = i; + break; + } + } + mutex_unlock(&pthread_mutex); + return result; +} + +int pthread_equal(pthread_t thread1, pthread_t thread2) +{ + return (thread1 == thread2); +} + +int pthread_cancel(pthread_t th) +{ + pthread_thread_t *other = pthread_sched_threads[th]; + if (!other) { + return -1; + } + + other->should_cancel = 1; + + return 0; +} + +int pthread_setcancelstate(int state, int *oldstate) +{ + (void) state; + (void) oldstate; + return 0; +} + +int pthread_setcanceltype(int type, int *oldtype) +{ + (void) type; + (void) oldtype; + return 0; +} + +void pthread_testcancel(void) +{ + pthread_t self = pthread_self(); + if (pthread_sched_threads[self]->should_cancel) { + pthread_exit(NULL); + } +} diff --git a/sys/posix/pthread/pthread_attr.c b/sys/posix/pthread/pthread_attr.c new file mode 100644 index 0000000000..9f4f524cda --- /dev/null +++ b/sys/posix/pthread/pthread_attr.c @@ -0,0 +1,157 @@ +/** + * POSIX implementation of threading. + * + * Copyright (C) 2013 Freie Universität Berlin + * + * This file subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup posix + * @{ + * @file pthread.c + * @brief Implementation of pthread. + * @author Christian Mehlis + * @author René Kijewski + * @} + */ + +#include + +#include "pthread.h" + +int pthread_attr_init(pthread_attr_t *attr) +{ + memset(attr, 0, sizeof (*attr)); + return 0; +} + +int pthread_attr_destroy(pthread_attr_t *attr) +{ + (void) attr; + return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) +{ + *detachstate = attr->detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE; + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) +{ + switch (detachstate) { + case PTHREAD_CREATE_DETACHED: + attr->detached = 1; + return 0; + case PTHREAD_CREATE_JOINABLE: + attr->detached = 0; + return 0; + default: + return -1; + } +} + +int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) +{ + // TODO + (void) attr; + (void) guardsize; + return -1; +} + +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) +{ + // TODO + (void) attr; + (void) guardsize; + return -1; +} + +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) +{ + // TODO + (void) attr; + (void) param; + return -1; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) +{ + // TODO + (void) attr; + (void) param; + return -1; +} + +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) +{ + // TODO + (void) attr; + (void) policy; + return -1; +} + +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +{ + // TODO + (void) attr; + (void) policy; + return -1; +} + +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit) +{ + // TODO + (void) attr; + (void) inherit; + return -1; +} + +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) +{ + // TODO + (void) attr; + (void) inherit; + return -1; +} + +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) +{ + // TODO + (void) attr; + (void) scope; + return -1; +} + +int pthread_attr_setscope(pthread_attr_t *attr, int scope) +{ + // TODO + (void) attr; + (void) scope; + return -1; +} + +int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) +{ + *stackaddr = attr->ss_sp; + return 0; +} + +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) +{ + attr->ss_sp = (char *) stackaddr; + return 0; +} + +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) +{ + *stacksize = attr->ss_size; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) +{ + attr->ss_size = stacksize; + return 0; +} diff --git a/sys/posix/pthread/pthread_mutex.c b/sys/posix/pthread/pthread_mutex.c new file mode 100644 index 0000000000..4ef99fe571 --- /dev/null +++ b/sys/posix/pthread/pthread_mutex.c @@ -0,0 +1,59 @@ +/** + * POSIX implementation of threading. + * + * Copyright (C) 2013 Freie Universität Berlin + * + * This file subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup posix + * @{ + * @file pthread.c + * @brief Implementation of pthread. + * @author Christian Mehlis + * @author René Kijewski + * @} + */ + +#include +#include + +#include "pthread.h" + +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) +{ + (void) mutexattr; + + return mutex_init(mutex); +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + (void) mutex; + + return 1; +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + return mutex_trylock(mutex); +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + return mutex_lock(mutex); +} + +int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime) +{ + (void) mutex; + (void) abstime; + return 0; /* currently not supported */ +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + mutex_unlock(mutex); + return 1; +} diff --git a/sys/posix/pthread/pthread_once.c b/sys/posix/pthread/pthread_once.c new file mode 100644 index 0000000000..7863516186 --- /dev/null +++ b/sys/posix/pthread/pthread_once.c @@ -0,0 +1,30 @@ +/** + * POSIX implementation of threading. + * + * Copyright (C) 2013 Freie Universität Berlin + * + * This file subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup posix + * @{ + * @file pthread.c + * @brief Implementation of pthread. + * @author Christian Mehlis + * @author René Kijewski + * @} + */ + +#include "pthread.h" + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + if (*once_control == PTHREAD_ONCE_INIT) { + init_routine(); + } + + *once_control = PTHREAD_ONCE_INIT + 1; + + return 0; +} diff --git a/sys/posix/pthread/pthread_spin.c b/sys/posix/pthread/pthread_spin.c new file mode 100644 index 0000000000..351eccabd5 --- /dev/null +++ b/sys/posix/pthread/pthread_spin.c @@ -0,0 +1,55 @@ +/** + * POSIX implementation of threading. + * + * Copyright (C) 2013 Freie Universität Berlin + * + * This file subject to the terms and conditions of the GNU Lesser General + * Public License. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup posix + * @{ + * @file pthread.c + * @brief Implementation of pthread. + * @author Christian Mehlis + * @author René Kijewski + * @} + */ + +#include "pthread.h" + +int pthread_spin_init(pthread_spinlock_t *lock, int pshared) +{ + (void) pshared; + *lock = 0; + return 0; +} + +int pthread_spin_destroy(pthread_spinlock_t *lock) +{ + (void) lock; + return 0; +} + +int pthread_spin_lock(pthread_spinlock_t *lock) +{ + while (*lock) { + ; /* spin */ + } + return 0; +} + +int pthread_spin_trylock(pthread_spinlock_t *lock) +{ + if (*lock) { + return 0; + } + + return -1; +} + +int pthread_spin_unlock(pthread_spinlock_t *lock) +{ + --*lock; + return 0; +} diff --git a/tests/test_pthread/Makefile b/tests/test_pthread/Makefile new file mode 100644 index 0000000000..d161e7b42f --- /dev/null +++ b/tests/test_pthread/Makefile @@ -0,0 +1,22 @@ +# name of your project +export PROJECT = test_pthread + +# for easy switching of boards +export BOARD ?= native + +# this has to be the absolute path of the RIOT-base dir +export RIOTBASE = $(CURDIR)/../.. + +## Modules to include. +USEMODULE += posix +USEMODULE += pthread + +ifeq ($(BOARD),native) + CFLAGS += -isystem $(RIOTBASE)/sys/posix/pthread/include +else + export INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include \ + -I$(RIOTBASE)/sys/posix/include +endif + +include $(RIOTBASE)/Makefile.include + diff --git a/tests/test_pthread/main.c b/tests/test_pthread/main.c new file mode 100644 index 0000000000..25f1e3031d --- /dev/null +++ b/tests/test_pthread/main.c @@ -0,0 +1,39 @@ +#include +#include + +#include "pthread.h" + +void *run(void *parameter) { + size_t n = (size_t) parameter; + size_t factorial = 1; + + printf("pthread: parameter = %u\n", (unsigned int) n); + + if (n > 0) { + for (size_t i = 1; i <= n; i++) { + factorial *= i; + } + } + + printf("pthread: factorial = %u\n", (unsigned int) factorial); + pthread_exit((void *)factorial); + + return NULL; +} + +int main(void) { + pthread_t th_id; + pthread_attr_t th_attr; + + size_t arg = 6; + printf("main parameter = %u\n", (unsigned int) arg); + + pthread_attr_init(&th_attr); + pthread_create(&th_id, &th_attr, run, (void *) arg); + size_t res; + pthread_join(th_id, (void **) &res); + printf("main: factorial = %u\n", (unsigned int) res); + puts("main: finished"); + return 0; +} +