Questions about Dynamic Threads


David Lerch
 

Hi,

I'm using Zephyr for the first time with the NRF9160, and I have a few
questions about Zephyr's support of threads:

1. Is there any support for runtime creation of a new thread's stack
in k_thread_create(), through the likes of malloc()? I only see
K_THREAD_STACK_DEFINE(), which allocate it at compile-time. I'm
porting an existing library to Zephyr that creates threads (and their
stacks) on-demand, and having compile-time-only stacks would mean that
I have to do some ugly changes to the APIs where stack pointers get
passed all the way through multiple API layers.

2. I currently work around this by creating "stack pools" through
K_THREAD_STACK_ARRAY_DEFINE(), where each stack can be reused by
another thread once one has finished executing (that's a bit of a
waste of memory though). I do this by locking and releasing these
stacks through an atomic in-use flag. It gets set for the stack before
the thread starts, and gets cleared by the parent thread right after
the thread's main function releases a semaphore at the end of
execution in a join-like operation. However, this fails when a new
thread reuses the stack right after the old thread has released it
with a message of "Illegal load of EXC_RETURN into PC". I assume
that's because the previous thread still needs the stack for a while
even after the last instruction in its main function (the release of
the join semaphore) was executed. It's "fixed" by sleeping for a short
while before starting a new thread with the stack, but that's
obviously a very unsafe hack. So how would I know when it's safe to
reuse the stack? Does Zephyr allow reusing stacks in this way at all?


Thanks,

David


Boie, Andrew P
 

1. Is there any support for runtime creation of a new thread's stack in
k_thread_create(), through the likes of malloc()? I only see
K_THREAD_STACK_DEFINE(), which allocate it at compile-time. I'm porting an
existing library to Zephyr that creates threads (and their
stacks) on-demand, and having compile-time-only stacks would mean that I
have to do some ugly changes to the APIs where stack pointers get passed all
the way through multiple API layers.
There isn't, due to the lack of a memory allocator which returns aligned pointers.

2. I currently work around this by creating "stack pools" through
K_THREAD_STACK_ARRAY_DEFINE(), where each stack can be reused by
another thread once one has finished executing (that's a bit of a waste of
memory though). I do this by locking and releasing these stacks through an
atomic in-use flag. It gets set for the stack before the thread starts, and gets
cleared by the parent thread right after the thread's main function releases a
semaphore at the end of execution in a join-like operation. However, this
fails when a new thread reuses the stack right after the old thread has
released it with a message of "Illegal load of EXC_RETURN into PC". I assume
that's because the previous thread still needs the stack for a while even after
the last instruction in its main function (the release of the join semaphore)
was executed. It's "fixed" by sleeping for a short while before starting a new
thread with the stack, but that's obviously a very unsafe hack. So how would
I know when it's safe to reuse the stack? Does Zephyr allow reusing stacks in
this way at all?
You sound like a good customer for the k_thread_join() API I recently sent a PR for:

https://github.com/zephyrproject-rtos/zephyr/pull/23028

-Andrew