Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

newlib: Initial thread-safe implementation #4529

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# pull dependencies from drivers
include $(RIOTBASE)/drivers/Makefile.dep

ifneq (,$(filter newlib_thread_safe,$(USEMODULE)))
USEMODULE += newlib_nano
endif

ifneq (,$(filter libcoap,$(USEPKG)))
USEMODULE += posix_sockets
USEMODULE += gnrc_conn_udp
Expand Down
7 changes: 7 additions & 0 deletions core/include/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#ifndef THREAD_H
#define THREAD_H

#if USE_NEWLIB_THREAD_SAFE
#include <sys/reent.h>
#endif

#include "clist.h"
#include "cib.h"
#include "msg.h"
Expand Down Expand Up @@ -93,6 +97,9 @@ struct _thread {
msg_t *msg_array; /**< memory holding messages */
#endif

#ifdef USE_NEWLIB_THREAD_SAFE
struct _reent newlib_reent; /**< thread's re-entrent object */
#endif
#if defined DEVELHELP || defined(SCHED_TEST_STACK)
char *stack_start; /**< thread's stack start address */
#endif
Expand Down
4 changes: 4 additions & 0 deletions core/sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ int sched_run(void)
sched_active_pid = next_thread->pid;
sched_active_thread = (volatile thread_t *) next_thread;

#ifdef USE_NEWLIB_THREAD_SAFE
_impure_ptr = &(next_thread->newlib_reent);
#endif

DEBUG("sched_run: done, changed sched_active_thread.\n");

return 1;
Expand Down
9 changes: 9 additions & 0 deletions core/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include <errno.h>
#include <stdio.h>

#ifdef USE_NEWLIB_THREAD_SAFE
#include <string.h>
#endif

#include "assert.h"
#include "thread.h"
#include "irq.h"
Expand Down Expand Up @@ -232,6 +236,11 @@ kernel_pid_t thread_create(char *stack, int stacksize, char priority, int flags,
cb->msg_array = NULL;
#endif

#ifdef USE_NEWLIB_THREAD_SAFE
/* initialize the reent context */
_REENT_INIT_PTR(&(cb->newlib_reent));
#endif

sched_num_threads++;

DEBUG("Created thread %s. PID: %" PRIkernel_pid ". Priority: %u.\n", name, cb->pid, priority);
Expand Down
8 changes: 8 additions & 0 deletions cpu/cortexm_common/include/cpu_conf_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,19 @@ extern "C" {
#define THREAD_EXTRA_STACKSIZE_PRINTF (512)
#endif
#ifndef THREAD_STACKSIZE_DEFAULT
#if defined(USE_NEWLIB_THREAD_SAFE)
#define THREAD_STACKSIZE_DEFAULT (1152)
#else
#define THREAD_STACKSIZE_DEFAULT (1024)
#endif
#endif
#ifndef THREAD_STACKSIZE_IDLE
#if defined(USE_NEWLIB_THREAD_SAFE)
#define THREAD_STACKSIZE_IDLE (384)
#else
#define THREAD_STACKSIZE_IDLE (256)
#endif
#endif
/** @} */

/**
Expand Down
3 changes: 3 additions & 0 deletions sys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ endif
ifneq (,$(filter sema,$(USEMODULE)))
DIRS += sema
endif
ifneq (,$(filter newlib_thread_safe,$(USEMODULE)))
DIRS += newlib/thread_safe
endif

DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))

Expand Down
4 changes: 4 additions & 0 deletions sys/Makefile.include
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ ifneq (,$(filter newlib_syscalls_default,$(USEMODULE)))
include $(RIOTBASE)/sys/newlib/Makefile.include
endif

ifneq (,$(filter newlib_thread_safe,$(USEMODULE)))
include $(RIOTBASE)/sys/newlib/thread_safe/Makefile.include
endif

ifneq (,$(filter arduino,$(USEMODULE)))
include $(RIOTBASE)/sys/arduino/Makefile.include
endif
Expand Down
3 changes: 3 additions & 0 deletions sys/newlib/thread_safe/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE = newlib_thread_safe

include $(RIOTBASE)/Makefile.base
22 changes: 22 additions & 0 deletions sys/newlib/thread_safe/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# define the compile test file
define __has_reent_small_test
#include <sys/reent.h>\n
#ifndef _REENT_SMALL\n
#error wasting 1KB\n
#endif
endef

# compile the tests
__has_reent := $(shell echo "\#include <sys/reent.h>\n" | $(CC) -E $(CFLAGS) $(INCLUDES) - 2> /dev/null > /dev/null; echo $$?)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the \n results in an error: "stdin>:1:23: error: extra tokens at end of #include directive [-Werror]", adding -e to the echo command solves it ( -e: enable interpretation of backslash escapes)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo is really the worst way to get a text string in shell scripts. There
is no standard specification and the flags and default behaviour differ
between almost every platform (OSX, Linux, BSD, windows etc.)
Also, the \n is redundant since echo will add a newline to the string (on
all platforms that I know of) when called with no flags.

Den 23 aug. 2016 09:52 skrev "Pieter Willemsen" notifications@github.com:

In sys/newlib/thread_safe/Makefile.include
#4529 (comment):

@@ -0,0 +1,22 @@
+# define the compile test file
+define __has_reent_small_test
+#include <sys/reent.h>\n
+#ifndef _REENT_SMALL\n
+#error wasting 1KB\n
+#endif
+endef
+
+# compile the tests
+__has_reent := $(shell echo "#include &lt;sys/reent.h&gt;\n" | $(CC) -E $(CFLAGS) $(INCLUDES) - 2> /dev/null > /dev/null; echo $$?)

the \n results in an error: "stdin>:1:23: error: extra tokens at end of
#include directive [-Werror]", adding -e to the echo command solves it (
-e: enable interpretation of backslash escapes)


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/RIOT-OS/RIOT/pull/4529/files/0b380225290d6b1efeea9a7bbb273684b417672d#r75816861,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AATYQu8sumUmNCJsn-omBi-6etbn_ofjks5qiqa9gaJpZM4G43Px
.

Copy link

@pwillem pwillem Aug 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the \n solved it.

__has_reent_small := $(shell echo "$(__has_reent_small_test)" | $(CC) -E $(CFLAGS) $(INCLUDES) - 2> /dev/null > /dev/null; echo $$?)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it isn't possible to remove the \n. Use printf instead of echo (-e)?


# check if we use REENT_SMALL
ifeq (0, $(__has_reent))
ifeq (0, $(__has_reent_small))
export USE_NEWLIB_THREAD_SAFE := 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the export doesn't generate an environment variable.
This pull request works when I change it to CFLAGS += -DUSE_NEWLIB_THREAD_SAFE

else
$(shell $(COLOR_ECHO) "\n$(COLOR_YELLOW)_REENT_SMALL not defined, thread-safe newlib disabled!\n$(COLOR_RESET)" 1>&2)
endif
else
$(shell $(COLOR_ECHO) "\n$(COLOR_YELLOW)MODULE_NEWLIB_THREAD_SAFE defined but <sys/reent.h> not found in toolchain path, thread-safe newlib is disabled!\n\n$(COLOR_RESET)\n" 1>&2)
endif
47 changes: 47 additions & 0 deletions sys/newlib/thread_safe/env_lock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 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 <nijzendoorn@engineering-spirit.nl>
*
* @}
*/

#include "newlib_lock.h"

#if USE_NEWLIB_THREAD_SAFE
static recursive_mutex_t _env = RECURSIVE_MUTEX_INIT;
#endif

/**
* @brief __env_lock needs to provide recursive mutex locking
*/
void __env_lock(struct _reent *_r)
{
(void) _r;

/* TODO another way would be to avoid rescheduling other tasks */
#if USE_NEWLIB_THREAD_SAFE
__recursive_lock(&_env);
#endif
}

void __env_unlock(struct _reent *_r)
{
(void) _r;

#if USE_NEWLIB_THREAD_SAFE
__recursive_unlock(&_env);
#endif
}
47 changes: 47 additions & 0 deletions sys/newlib/thread_safe/malloc_lock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 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 <nijzendoorn@engineering-spirit.nl>
*
* @}
*/

#include "newlib_lock.h"

#if USE_NEWLIB_THREAD_SAFE
static recursive_mutex_t _malloc = RECURSIVE_MUTEX_INIT;
#endif

/**
* @brief __malloc_lock needs to provide recursive mutex locking
*/
void __malloc_lock(struct _reent *_r)
{
(void) _r;

/* TODO another way would be to avoid rescheduling other tasks */
#if USE_NEWLIB_THREAD_SAFE
__recursive_lock(&_malloc);
#endif
}

void __malloc_unlock(struct _reent *_r)
{
(void) _r;

#if USE_NEWLIB_THREAD_SAFE
__recursive_unlock(&_malloc);
#endif
}
86 changes: 86 additions & 0 deletions sys/newlib/thread_safe/newlib_lock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2016 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 recursive implementation
*
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
*
* @}
*/

#include <assert.h>

#include "arch/irq_arch.h"
#include "newlib_lock.h"

void __recursive_init(recursive_mutex_t *rm)
{
recursive_mutex_t empty_mutex = RECURSIVE_MUTEX_INIT;
*rm = empty_mutex;
}

/**
* @brief __recursive_lock needs to provide recursive mutex locking
*/
void __recursive_lock(recursive_mutex_t *rm)
{
/* TODO another way would be to avoid rescheduling other tasks */
#if USE_NEWLIB_THREAD_SAFE

/* check if the mutex lock is not called from an interrupt */
assert(!(irq_arch_in()));

/* try to lock the recursive mutex */
switch (mutex_trylock(&rm->mutex))
{
case 0:
/* mutex is already locked */
if (rm->owner != thread_getpid()) {
/* we are not the owner, so we wait till it's released and
* fall-trough the case statement */
mutex_lock(&rm->mutex);
}

/* fall-trough */

case 1:
/* mutex now locked by us */
atomic_inc(&rm->recursion);
rm->owner = thread_getpid();
break;
}
#else
(void) rm;
#endif
}

void __recursive_unlock(recursive_mutex_t *rm)
{
#if USE_NEWLIB_THREAD_SAFE
/* check if the mutex unlock is not called from an interrupt */
assert(!(irq_arch_in()));

/* check if the unlocking thread is the same as the locking thread */
assert(rm->owner == thread_getpid());

/* decrease the recursion counter */
if (atomic_dec(&rm->recursion) == 1) {
/* we just released the last recursion lock call */

rm->owner = KERNEL_PID_UNDEF;
mutex_unlock(&rm->mutex);
}
#else
(void) rm;
#endif
}
77 changes: 77 additions & 0 deletions sys/newlib/thread_safe/newlib_lock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2016 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 recursive lock implementation
*
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
*
* @}
*/

#ifndef THREAD_SAFE_NEWLIB_LOCK_H_
#define THREAD_SAFE_NEWLIB_LOCK_H_

#include <sys/reent.h>

#include <thread.h>
#include <mutex.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief An recursive mutex implementation that can be used for newlib's
* thread-safe implementation
*/
typedef struct recursive_mutex
{
volatile kernel_pid_t owner; /**< owner of the recursive mutex */
atomic_int_t recursion; /**< number of recursive lock calls */
mutex_t mutex; /**< mutex that is locked recursively */
} recursive_mutex_t;

/**
* @brief Initialization values for a recursive mutex
*/
#define RECURSIVE_MUTEX_INIT { KERNEL_PID_UNDEF, { 0 }, MUTEX_INIT }

/**
* @brief initialize the recursive mutex if it's not yet initialized
*
* @param[in] rm recursive mutex structure
*/
void __recursive_init(recursive_mutex_t *rm);

/**
* @brief lock the mutex recursively, try to lock the mutex.
* - If the mutex is free claim ownership.
* - If the mutex is locked by us already; increase the recursion count
* - If the mutex is locked by another task; wait for the mutex to be freed
*
* @param[in] rm recursive mutex structure
*/
void __recursive_lock(recursive_mutex_t *rm);

/**
* @brief release the mutex recursively, if the last lock is freeed release the mutex
*
* @param[in] rm recursive mutex structure
*/
void __recursive_unlock(recursive_mutex_t *rm);

#ifdef __cplusplus
};
#endif

#endif /* THREAD_SAFE_NEWLIB_LOCK_H_ */
Loading