From 3f53d584fecae676ce1add23ede2972cd891ae93 Mon Sep 17 00:00:00 2001 From: DipSwitch Date: Sun, 20 Dec 2015 18:03:10 +0100 Subject: [PATCH] newlib: Initial thread-safe implementation --- Makefile.dep | 4 ++ core/include/tcb.h | 3 ++ core/sched.c | 4 ++ sys/newlib/thread_safe/Makefile | 3 ++ sys/newlib/thread_safe/env_lock.c | 74 ++++++++++++++++++++++++++++ sys/newlib/thread_safe/malloc_lock.c | 74 ++++++++++++++++++++++++++++ 6 files changed, 162 insertions(+) create mode 100644 sys/newlib/thread_safe/Makefile create mode 100644 sys/newlib/thread_safe/env_lock.c create mode 100644 sys/newlib/thread_safe/malloc_lock.c diff --git a/Makefile.dep b/Makefile.dep index 697a5644614dc..f5a70e757a8e6 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -1,6 +1,10 @@ # pull dependencies from drivers include $(RIOTBASE)/drivers/Makefile.dep +ifneq (,$(filter newlib_thread_safe,$(USEMODULE))) + USEMODULE += newlib +endif + ifneq (,$(filter libcoap,$(USEPKG))) USEMODULE += posix_sockets endif diff --git a/core/include/tcb.h b/core/include/tcb.h index 41a803c5bf8a6..59017c1d9ab41 100644 --- a/core/include/tcb.h +++ b/core/include/tcb.h @@ -81,6 +81,9 @@ typedef struct tcb_t { const char *name; /**< thread's name */ int stack_size; /**< thread's stack size */ #endif +#ifdef MODULE_NEWLIB_THREAD_SAFE + struct reent newlib_reent; /**< thread's re-entrent object */ +#endif } tcb_t; #ifdef __cplusplus diff --git a/core/sched.c b/core/sched.c index 2429496bdcfce..f1d018c81eb98 100644 --- a/core/sched.c +++ b/core/sched.c @@ -117,6 +117,10 @@ int sched_run(void) sched_active_pid = next_thread->pid; sched_active_thread = (volatile tcb_t *) next_thread; +#ifdef MODULE_NEWLIB_THREAD_SAFE + _impure_ptr = &(sched_active_thread->newlib_reent); +#endif + DEBUG("sched_run: done, changed sched_active_thread.\n"); return 1; diff --git a/sys/newlib/thread_safe/Makefile b/sys/newlib/thread_safe/Makefile new file mode 100644 index 0000000000000..3404d1463a404 --- /dev/null +++ b/sys/newlib/thread_safe/Makefile @@ -0,0 +1,3 @@ +MODULE = newlib_thread_safe + +include $(RIOTBASE)/Makefile.base diff --git a/sys/newlib/thread_safe/env_lock.c b/sys/newlib/thread_safe/env_lock.c new file mode 100644 index 0000000000000..5a31149bf7fa4 --- /dev/null +++ b/sys/newlib/thread_safe/env_lock.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Engineering-Spirit + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup sys_newlib + * @{ + * + * @file + * @brief Newlib thread-safe env implementation + * + * @author Nick v. IJzendoorn + * + * @} + */ + +#include + +#include +#include + +#if MODULE_NEWLIB_THREAD_SAFE +static volatile kernel_pid_t _env_owner = KERNEL_PID_UNDEF; +static atomic_int_t _env_lock_recursion; +static mutex_t _env_mutex; +#endif + +/** + * @brief __env_lock needs to provide recursive mutex locking + */ +void __env_lock(struct _reent *_r) +{ + /* TODO another way would be to avoid rescheduling other tasks */ +#if MODULE_NEWLIB_THREAD_SAFE + switch (mutex_trylock(&_env_mutex)) + { + case 0: + /* mutex is already locked */ + if (_env_owner != thread_getpid()) { + /* we are not the owner, so we wait till it's released and + * fall-trough the case statement */ + mutex_lock(&_env_mutex); + } + + continue; + + case 1: + /* mutex now locked by us */ + atomic_inc(&_env_lock_recursion); + _env_owner = thread_getpid(); + break; + } +#else + (void) _r; +#endif +} + +void __env_unlock(struct _reent *_r) +{ +#if MODULE_NEWLIB_THREAD_SAFE + if (atomic_dec(&_env_lock_recursion) == 1) { + /* we just released the last recursion lock call */ + + _env_owner = KERNEL_PID_UNDEF; + mutex_unlock(&_env_mutex); + } +#else + (void) _r; +#endif +} diff --git a/sys/newlib/thread_safe/malloc_lock.c b/sys/newlib/thread_safe/malloc_lock.c new file mode 100644 index 0000000000000..3f503dd3a3a40 --- /dev/null +++ b/sys/newlib/thread_safe/malloc_lock.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Engineering-Spirit + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup sys_newlib + * @{ + * + * @file + * @brief Newlib thread-safe malloc implementation + * + * @author Nick v. IJzendoorn + * + * @} + */ + +#include + +#include +#include + +#if MODULE_NEWLIB_THREAD_SAFE +static volatile kernel_pid_t _malloc_owner = KERNEL_PID_UNDEF; +static atomic_int_t _malloc_lock_recursion; +static mutex_t _malloc_mutex; +#endif + +/** + * @brief __malloc_lock needs to provide recursive mutex locking + */ +void __malloc_lock(struct _reent *_r) +{ + /* TODO another way would be to avoid rescheduling other tasks */ +#if MODULE_NEWLIB_THREAD_SAFE + switch (mutex_trylock(&_malloc_mutex)) + { + case 0: + /* mutex is already locked */ + if (_malloc_owner != thread_getpid()) { + /* we are not the owner, so we wait till it's released and + * fall-trough the case statement */ + mutex_lock(&_malloc_mutex); + } + + continue; + + case 1: + /* mutex now locked by us */ + atomic_inc(&_malloc_lock_recursion); + _malloc_owner = thread_getpid(); + break; + } +#else + (void) _r; +#endif +} + +void __malloc_unlock(struct _reent *_r) +{ +#if MODULE_NEWLIB_THREAD_SAFE + if (atomic_dec(&_malloc_lock_recursion) == 1) { + /* we just released the last recursion lock call */ + + _malloc_owner = KERNEL_PID_UNDEF; + mutex_unlock(&_malloc_mutex); + } +#else + (void) _r; +#endif +}