diff options
| -rw-r--r-- | ports/esp32/mpthreadport.c | 5 | ||||
| -rw-r--r-- | tests/thread/thread_coop.py | 53 | ||||
| -rw-r--r-- | tests/thread/thread_coop.py.exp | 2 |
3 files changed, 60 insertions, 0 deletions
diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index 12a6ede86..34fef9f7b 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -221,6 +221,11 @@ int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { xSemaphoreGive(mutex->handle); + // Python threads run at equal priority, so pre-emptively yield here to + // prevent pathological imbalances where a thread unlocks and then + // immediately re-locks a mutex before a context switch can occur, leaving + // another thread waiting for an unbounded period of time. + taskYIELD(); } void mp_thread_deinit(void) { diff --git a/tests/thread/thread_coop.py b/tests/thread/thread_coop.py new file mode 100644 index 000000000..aefc4af07 --- /dev/null +++ b/tests/thread/thread_coop.py @@ -0,0 +1,53 @@ +# Threads should be semi-cooperative, to the point where one busy +# thread can't starve out another. +# +# (Note on ports without the GIL this one should always be true, on ports with GIL it's +# a test of the GIL behaviour.) + +import _thread +import sys +from time import ticks_ms, ticks_diff, sleep_ms + + +done = False + +ITERATIONS = 5 +SLEEP_MS = 250 +MAX_DELTA = 30 + +if sys.platform in ("win32", "linux", "darwin"): + # Conventional operating systems get looser timing restrictions + SLEEP_MS = 300 + MAX_DELTA = 100 + + +def busy_thread(): + while not done: + pass + + +def test_sleeps(): + global done + ok = True + for _ in range(ITERATIONS): + t0 = ticks_ms() + sleep_ms(SLEEP_MS) + t1 = ticks_ms() + d = ticks_diff(t1, t0) + if d < SLEEP_MS - MAX_DELTA or d > SLEEP_MS + MAX_DELTA: + print("Slept too long ", d) + ok = False + print("OK" if ok else "Not OK") + done = True + + +# make the thread the busy one, and check sleep time on main task +_thread.start_new_thread(busy_thread, ()) +test_sleeps() + +sleep_ms(100) +done = False + +# now swap them +_thread.start_new_thread(test_sleeps, ()) +busy_thread() diff --git a/tests/thread/thread_coop.py.exp b/tests/thread/thread_coop.py.exp new file mode 100644 index 000000000..2c94e4837 --- /dev/null +++ b/tests/thread/thread_coop.py.exp @@ -0,0 +1,2 @@ +OK +OK |
