summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/esp32/mpthreadport.c5
-rw-r--r--tests/thread/thread_coop.py53
-rw-r--r--tests/thread/thread_coop.py.exp2
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