Skip to content

Commit 110d6bb

Browse files
committed
extmod/zephyr_kernel,py: Refactor threading to match ports/zephyr pattern.
Major architectural change to align with proven ports/zephyr implementation: 1. Removed MICROPY_ZEPHYR_THREADING guards from py/modthread.c and py/runtime.h 2. Simplified zephyr_entry() to minimal wrapper (just calls entry function) 3. Removed heap allocation of mp_state_thread_t (now stack-allocated in thread_entry) 4. Changed from K_FOREVER to K_NO_WAIT for immediate thread start 5. Removed manual scheduler manipulation (z_mark_thread_as_not_sleeping, z_ready_thread) This makes extmod/zephyr_kernel follow the same pattern as ports/zephyr, where thread_entry() in py/modthread.c handles all state initialization with a local mp_state_thread_t variable on the thread's stack. Testing shows threads can execute loops and access globals correctly with this cleaner architecture. Signed-off-by: Andrew Leech <[email protected]>
1 parent 6ee27fc commit 110d6bb

File tree

3 files changed

+5
-90
lines changed

3 files changed

+5
-90
lines changed

extmod/zephyr_kernel/kernel/mpthread_zephyr.c

Lines changed: 5 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ typedef struct _mp_thread_t {
7373
void *arg; // Python args (GC root pointer)
7474
void *stack; // Stack pointer
7575
size_t stack_len; // Stack size in words
76-
mp_state_thread_t *thread_state;// Python thread state (GC root)
7776
struct _mp_thread_t *next; // Next in linked list
7877
} mp_thread_t;
7978

@@ -103,7 +102,6 @@ bool mp_thread_init(void *stack) {
103102
thread_entry0.arg = NULL;
104103
thread_entry0.stack = stack;
105104
thread_entry0.stack_len = 0; // Bootstrap thread uses C runtime stack, size unknown
106-
thread_entry0.thread_state = &mp_state_ctx.thread; // Main thread uses global state
107105
thread_entry0.next = NULL;
108106

109107
k_thread_name_set(thread_entry0.id, "mp_main");
@@ -190,7 +188,6 @@ void mp_thread_gc_others(void) {
190188

191189
gc_collect_root((void **)&th, 1);
192190
gc_collect_root(&th->arg, 1);
193-
gc_collect_root((void **)&th->thread_state, 1); // Scan thread state
194191

195192
if (th->id == k_current_get()) {
196193
continue; // Don't scan current thread's stack (done separately)
@@ -239,59 +236,11 @@ void mp_thread_start(void) {
239236
static void zephyr_entry(void *arg1, void *arg2, void *arg3) {
240237
(void)arg3;
241238

242-
// Get the mp_thread_t structure for this thread (passed via arg3 actually stored in custom_data)
243-
// Find our thread in the list
244-
mp_thread_t *self = NULL;
245-
k_tid_t current_tid = k_current_get();
246-
247-
mp_thread_mutex_lock(&thread_mutex, 1);
248-
for (mp_thread_t *th = thread; th != NULL; th = th->next) {
249-
if (th->id == current_tid) {
250-
self = th;
251-
break;
252-
}
253-
}
254-
mp_thread_mutex_unlock(&thread_mutex);
255-
256-
if (self == NULL || self->thread_state == NULL) {
257-
// Fatal error - thread state not set up
258-
k_thread_abort(current_tid);
259-
for (;;) { ; }
260-
}
261-
262-
// Set up thread-local storage using pre-allocated state
263-
mp_thread_set_state(self->thread_state);
264-
265-
// Initialize thread state fields
266-
self->thread_state->gc_lock_depth = 0;
267-
self->thread_state->nlr_top = NULL;
268-
self->thread_state->nlr_jump_callback_top = NULL;
269-
self->thread_state->mp_pending_exception = MP_OBJ_NULL;
270-
271-
// Inherit globals/locals from main thread
272-
mp_locals_set(mp_state_ctx.thread.dict_locals);
273-
mp_globals_set(mp_state_ctx.thread.dict_globals);
274-
275-
// Get stack info from current thread
276-
struct k_thread *current = k_current_get();
277-
void *stack_top = (void *)((uintptr_t)current->stack_info.start + current->stack_info.size);
278-
size_t stack_limit = current->stack_info.size - 1024; // Leave margin for Zephyr
279-
280-
// Set up stack bounds for Python stack checking
281-
mp_stack_set_top(stack_top);
282-
mp_stack_set_limit(stack_limit);
283-
284-
// Mark thread as started (updates status to READY)
285-
mp_thread_start();
286-
287-
// Now safe to execute Python code
239+
// arg1 contains the python thread entry point
288240
if (arg1) {
289241
void *(*entry)(void *) = arg1;
290242
entry(arg2);
291243
}
292-
293-
// Clean up when thread finishes
294-
mp_thread_finish();
295244
k_thread_abort(k_current_get());
296245
for (;;) {
297246
; // Never reached
@@ -310,36 +259,32 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s
310259
// Try to garbage collect old threads
311260
gc_collect();
312261

313-
// Allocate thread node and thread state (must be outside mutex lock for GC)
262+
// Allocate thread node (must be outside mutex lock for GC)
314263
mp_thread_t *th = m_new_obj(mp_thread_t);
315-
mp_state_thread_t *ts = m_new_obj(mp_state_thread_t);
316-
memset(ts, 0, sizeof(mp_state_thread_t));
317264

318265
mp_thread_mutex_lock(&thread_mutex, 1);
319266

320267
// Find available stack slot
321268
int32_t _slot = mp_thread_find_stack_slot();
322269
if (_slot < 0) {
323270
mp_thread_mutex_unlock(&thread_mutex);
324-
m_del_obj(mp_state_thread_t, ts); // Clean up allocated memory
325271
m_del_obj(mp_thread_t, th);
326272
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("maximum number of threads reached"));
327273
}
328274

329-
// Create Zephyr thread with K_FOREVER so it stays unscheduled initially
330-
// We'll manually wake it and add to ready queue following official Zephyr pattern
275+
// Create Zephyr thread with K_NO_WAIT to start immediately
276+
// This matches the ports/zephyr pattern
331277
th->id = k_thread_create(
332278
&th->z_thread,
333279
mp_thread_stack_array[_slot],
334280
K_THREAD_STACK_SIZEOF(mp_thread_stack_array[_slot]),
335281
zephyr_entry,
336282
entry, arg, NULL,
337-
priority, 0, K_FOREVER
283+
priority, 0, K_NO_WAIT
338284
);
339285

340286
if (th->id == NULL) {
341287
mp_thread_mutex_unlock(&thread_mutex);
342-
m_del_obj(mp_state_thread_t, ts); // Clean up allocated memory
343288
m_del_obj(mp_thread_t, th);
344289
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("can't create thread"));
345290
}
@@ -353,7 +298,6 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s
353298
th->arg = arg;
354299
th->stack = (void *)th->z_thread.stack_info.start;
355300
th->stack_len = th->z_thread.stack_info.size / sizeof(uintptr_t);
356-
th->thread_state = ts; // Store pre-allocated thread state
357301
th->next = thread;
358302
thread = th;
359303

@@ -365,18 +309,7 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s
365309

366310
DEBUG_printf("Created thread %s (id=%p)\n", name, th->id);
367311

368-
// Mark thread as not sleeping, then UNLOCK MUTEX before adding to ready queue
369-
// This follows official Zephyr's prepare_multithreading() pattern (init.c:467-468)
370-
// CRITICAL: Must unlock BEFORE z_ready_thread() to allow context switch to occur
371-
// Note: z_ready_thread() uses K_SPINLOCK internally for IRQ protection
372-
z_mark_thread_as_not_sleeping(th->id);
373312
mp_thread_mutex_unlock(&thread_mutex);
374-
z_ready_thread(th->id);
375-
376-
// Trigger context switch to give new thread a chance to run
377-
// This just sets PendSV which will fire when we return to thread mode
378-
extern void mp_zephyr_arch_yield(void);
379-
mp_zephyr_arch_yield();
380313

381314
return (mp_uint_t)th->id;
382315
}

py/modthread.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,6 @@ static void *thread_entry(void *args_in) {
158158

159159
thread_entry_args_t *args = (thread_entry_args_t *)args_in;
160160

161-
#if !MICROPY_ZEPHYR_THREADING
162-
// For Zephyr threading, thread state is already initialized by zephyr_entry()
163-
// using heap-allocated state. Do NOT reinitialize here with local variable.
164161
mp_state_thread_t ts;
165162
mp_thread_init_state(&ts, args->stack_size, args->dict_locals, args->dict_globals);
166163

@@ -169,23 +166,15 @@ static void *thread_entry(void *args_in) {
169166
mp_obj_t mini_pystack[128];
170167
mp_pystack_init(mini_pystack, &mini_pystack[128]);
171168
#endif
172-
#endif
173169

174170
MP_THREAD_GIL_ENTER();
175171

176-
#if !MICROPY_ZEPHYR_THREADING
177-
// For Zephyr threading, mp_thread_start() is already called by zephyr_entry()
178172
mp_thread_start();
179-
#endif
180173

181174
// TODO set more thread-specific state here:
182175
// cur_exception (root pointer)
183176

184-
#if !MICROPY_ZEPHYR_THREADING
185177
DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top));
186-
#else
187-
DEBUG_printf("[thread] start args=%p stack=%p\n", &args, MP_STATE_THREAD(stack_top));
188-
#endif
189178

190179
nlr_buf_t nlr;
191180
if (nlr_push(&nlr) == 0) {

py/runtime.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,7 @@ void mp_call_function_1_from_nlr_jump_callback(void *ctx_in);
159159
static inline void mp_thread_init_state(mp_state_thread_t *ts, size_t stack_size, mp_obj_dict_t *locals, mp_obj_dict_t *globals) {
160160
mp_thread_set_state(ts);
161161

162-
#if !MICROPY_ZEPHYR_THREADING
163-
// For Zephyr threading, C stack is already initialized by zephyr_entry()
164-
// using actual Zephyr thread stack info. Do NOT reinitialize here.
165162
mp_cstack_init_with_top(ts + 1, stack_size); // need to include ts in root-pointer scan
166-
#else
167-
// Zephyr threading: stack_top/stack_limit already set correctly by zephyr_entry()
168-
(void)stack_size; // Suppress unused parameter warning
169-
#endif
170163

171164
// GC starts off unlocked
172165
ts->gc_lock_depth = 0;

0 commit comments

Comments
 (0)