-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
RFC: libc: thread-safe newlib #21519
Comments
For the build options used by the GNU ARM Embedded newlib, refer to the following comment: |
I see that crosstool-ng, which is used to build newlib for the Zephyr SDK, does not provide a Kconfig option to enable https://github.com/crosstool-ng/crosstool-ng/blob/master/config/libc/newlib.in
It looks like the GNU ARM Embedded enables This option is specified through crosstool-ng config For the Zephyr SDK 0.11.0 release, it will have the same config as the GNU ARM Embedded (i.e. |
These bindings are old, I've touched this file a lot and the basic implementation pre-dates my involvement in the project, this is code from when the OS was closed source and known as Viper. So file under "nobody knew" probably.
People who are on really constrained systems will usually use minimal libc anyway.
We currently do errno in a thread-local storage area carved out of the stack buffer. The os function is z_errno(). This gets mapped onto newlib hook __errno. See kernel/errno.c and newlib libc-hooks. We need a better thread-local storage implementation in Zephyr, right now to get at it you have to make a system call and we don't support __thread.
A one-count semaphore is the right thing to do if you don't need recursive entry or priority inheritance.
This doesn't seem SMP-safe at first glance.
Yeah this has to be in the arch code if we want this updated on context switch. For example if you take an interrupt, do something in it which causes a new thread to be runnable at IRQ exit arch_swap gets called directly from the arch code, no calls are made into common code (although perhaps we can change this, we could move the z_check_stack_sentinel invocations there too) For arches that use CONFIG_SWITCH, you might be able to put the hook in z_get_next_switch_handle(). |
I feel like this should be reclassified as a bug, @nashif do you agree? |
The newlib thread safety issue will be fully addressed by the PR #36201, which implements the retargetable locking interface to provide locks for the newlib, and this issue can be closed once that PR is merged. As for the reentrancy, it is really a non-issue at the moment because the current Zephyr-to-newlib integration scheme does not support any features that actually require reentrancy support, and multiple threads can safely call the newlib functions without it. The reentrancy support will be required in the future when we implement more advanced form of newlib integration (e.g. newlib-provided POSIX functions, newlib-provided |
Build newlib library to be thread-safe in multithreaded environment. zephyrproject-rtos/zephyr#21518 zephyrproject-rtos/zephyr#21519 zephyrproject-rtos/zephyr#36201 https://sourceware.org/legacy-ml/newlib/2016/msg01165.html https://sourceware.org/git/?p=newlib-cygwin.git;a=commit;h=bd54749095ee45d7136b6e7c8a1e5218749c87b6 Error log: newlib/libc-hooks.c:310:1: note: in expansion of macro 'BUILD_ASSERT' BUILD_ASSERT(IS_ENABLED(_RETARGETABLE_LOCKING), "Retargetable locking must be enabled"); Signed-off-by: Naveen Saini <[email protected]> Tested-by: Jon Mason <[email protected]>
Introduction
Extend Zephyr to be able to use the C standard library newlib in a multi-threaded environment.
Problem description
The minimum requirement to use libstdc++ in an RTOS environment is to have a thread-safe C library at hand – which is, from my point of view, regarding Zephyr’s adoption of newlib not the case today.
Proposed change
Implement the hooks provided by newlib to allow its use in a thread-safe manner.
Detailed RFC
Despite the fact that there's a scheduler, none of the requirements to use newlib in a multi-threaded environment have been fullfilled.
Reentrancy
Newlib manages multi-threaded applications with the help of structs of type
_reent
, which serve as a constant data container between library calls. In addition to that, stubs to allow an RTOS to implement reentrant versions of the standard functions (open_r
,write_r
, etc.) are provided. Simplified, in a multi-threaded environment each thread should have its own_reent
struct. It should then either call the reentrant library functions and pass it's_reent
struct or set up the global_reent
structimpure_ptr
and use the non-reentrant library functions.Today, the non-reentrant library functions are used and all threads operate on the same global
_reent
structimpure_ptr
defined within newlib.Malloc Hooks
Issue #17552 aims to provide thread-safety for malloc by adding a semaphore within
sbrk
. Newlib has addressed this issue by providing the hooks__malloc_lock
and__malloc_unlock
. The advantage of implementing them is that they guard also the calling code, not onlysbrk
.Retarget Locking
Newlib internally uses mutex synchronization quite often. At the moment empty stubs are used as Zephyr does not provide an implementation. Therefore, there’s no synchronization at all. The stubs can be replaced at application build time if newlib was build with
--enable-newlib-retargetable-locking
. In such a scenario several__retarget_lock_*
functions to create, delete, and operate recursive and non-recursive mutex become available for reimplementation. In addition to the mutex created during runtime by newlib one has to provide nine static locks.GNU ARM Embedded toolchain is build using the switch above, but Zephyr SDK is not doing that yet.
Proposed change (Detailed)
Reentrancy
According to the documentation of newlib there are two ways to achieve reentrancy. Both require each thread of execution control to initialize a unique global variable of type struct
_reent
:Option 1 would require us to always call the reentrant functions such as
malloc_r
instead ofmalloc
. On the one hand, this has to be explained to every user to make sure they call the right functions. On the other hand, 3rd party code can not be simply dropped in without modifications. Therefore, I propose to implement option 2.Proposed change:
reent
struct to eachk_thread
structreent
structimpure_ptr
to point to thread'sreent
struct after context switchMalloc Hooks / Retarget Locking
Proposed change:
sbrk
_RETARGETABLE_LOCKING
is present. Depending on this define make either__malloc_lock
and__malloc_unlock
or__retarget_lock_*
functions visible and add the static locks (there's one for malloc, too)CONFIG_
to adjust the size of the memory to be reserverd__malloc_lock
and__malloc_unlock
sys_mem_pool
Dependencies
--enable-newlib-retargetable-locking
--enable-newlib-reent-small
(AFAIK GNU ARM Embedded + Zephyr SDK have that set)Concerns and Unresolved Questions
General
Implementation
arch_swap
the right place for setting up impure_ptr after context switch? (Done only for ARM until there’s some feedback.)reent
around withink_thread
has obviously some impact on memory consumption, but I have no better idea (except saying "Well, use reent_small")Implementation
PR #21518
Relevant Links
Documentation
Newlib Documentation on __malloc_lock
Newlib Documentation on Reentrancy
Mailing Lists
Newlib discussion on
--enable-newlib-retargetable-locking
[PATCH, newlib] Allow locking routine to be retargeted
What are the __retarget_lock functions?
Alternatives
Go the RTEMS - way and extend newlib / GCC with a port for Zephyr. That would also give us the opportunity to have OpenMP and a thread-safe libstdc++, which would be striking.
The text was updated successfully, but these errors were encountered: