micropython/tests/thread
Angus Gratton 337742f6c7 esp32/mpthreadport: Fix uneven GIL allocation between Python threads.
Explicitly yield each time a thread mutex is unlocked.

Key to understanding this bug is that Python threads run at equal RTOS
priority, and although ESP-IDF FreeRTOS (and I think vanilla FreeRTOS)
scheduler will round-robin equal priority tasks in the ready state it does
not make a similar guarantee for tasks moving between ready and waiting.

The pathological case of this bug is when one Python thread task is busy
(i.e. never blocks) it will hog the CPU more than expected, sometimes for
an unbounded amount of time. This happens even though it periodically
unlocks the GIL to allow another task to run.

Assume T1 is busy and T2 is blocked waiting for the GIL. T1 is executing
and hits a condition to yield execution:

1. T1 calls MP_THREAD_GIL_EXIT
2. FreeRTOS sees T2 is waiting for the GIL and moves it to the Ready list
   (but does not preempt, as T2 is same priority, so T1 keeps running).
3. T1 immediately calls MP_THREAD_GIL_ENTER and re-takes the GIL.
4. Pre-emptive context switch happens, T2 wakes up, sees GIL is not
   available, and goes on the waiting list for the GIL again.

To break this cycle step 4 must happen before step 3, but this may be a
very narrow window of time so it may not happen regularly - and
quantisation of the timing of the tick interrupt to trigger a context
switch may mean it never happens.

Yielding at the end of step 2 maximises the chance for another task to run.

Adds a test that fails on esp32 before this fix and passes afterwards.

Fixes issue #15423.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2024-07-23 12:33:19 +10:00
..
disable_irq.py rp2: Fix recursive atomic sections when core1 is active. 2024-06-25 11:01:25 +10:00
disable_irq.py.exp rp2: Fix recursive atomic sections when core1 is active. 2024-06-25 11:01:25 +10:00
mutate_bytearray.py top: Update Python formatting to black "2023 stable style". 2023-02-02 12:51:03 +11:00
mutate_dict.py top: Update Python formatting to black "2023 stable style". 2023-02-02 12:51:03 +11:00
mutate_instance.py top: Update Python formatting to black "2023 stable style". 2023-02-02 12:51:03 +11:00
mutate_list.py top: Update Python formatting to black "2023 stable style". 2023-02-02 12:51:03 +11:00
mutate_set.py top: Update Python formatting to black "2023 stable style". 2023-02-02 12:51:03 +11:00
stress_aes.py tests/thread/stress_aes.py: Fix logic waiting for finished threads. 2024-07-05 17:07:27 +10:00
stress_create.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
stress_heap.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
stress_recurse.py
stress_schedule.py tests/thread: Re-enable GC before stress_schedule test ends. 2024-06-21 14:45:59 +10:00
stress_schedule.py.exp
thread_coop.py esp32/mpthreadport: Fix uneven GIL allocation between Python threads. 2024-07-23 12:33:19 +10:00
thread_coop.py.exp esp32/mpthreadport: Fix uneven GIL allocation between Python threads. 2024-07-23 12:33:19 +10:00
thread_exc1.py tests/thread: Make exc1,exit1,exit2,stacksize1,start1 tests run on rp2. 2021-05-10 13:07:16 +10:00
thread_exc2.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
thread_exc2.py.exp
thread_exit1.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
thread_exit2.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
thread_gc1.py tests/thread: Adjust thread tests so most are able to run on rp2 port. 2024-01-05 10:02:27 +11:00
thread_heap_lock.py
thread_heap_lock.py.exp
thread_ident1.py tests/thread: Adjust thread tests so most are able to run on rp2 port. 2024-01-05 10:02:27 +11:00
thread_lock1.py all: Fix spelling mistakes based on codespell check. 2023-04-27 18:03:06 +10:00
thread_lock2.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
thread_lock3.py
thread_lock4.py tests/thread: Adjust thread tests so most are able to run on rp2 port. 2024-01-05 10:02:27 +11:00
thread_lock5.py
thread_qstr1.py tests/thread: Adjust thread tests so most are able to run on rp2 port. 2024-01-05 10:02:27 +11:00
thread_shared1.py tests/thread: Adjust thread tests so most are able to run on rp2 port. 2024-01-05 10:02:27 +11:00
thread_shared2.py
thread_sleep1.py tests/thread: Adjust thread tests so most are able to run on rp2 port. 2024-01-05 10:02:27 +11:00
thread_sleep2.py tests/thread: Add a test for accuracy of sleep within a thread. 2024-01-05 10:35:34 +11:00
thread_stacksize1.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
thread_start1.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00
thread_start2.py tests: Replace umodule with module everywhere. 2023-06-08 17:54:24 +10:00