From b070765427283fd994f217b8c7601ef9b1f9a37a Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 13 Jul 2025 22:49:15 +1000 Subject: [PATCH] tests/thread: Allow thread tests to pass with the native emitter. The native emitter will not release/bounce the GIL when running code, so if it runs tight loops then no other threads get a chance to run (if the GIL is enabled). So for the thread tests, explicitly include a call to `time.sleep(0)` (or equivalent) to bounce the GIL and give other threads a chance to run. For some tests (eg `thread_coop.py`) the whole point of the test is to test that the GIL is correctly bounced. So for those cases force the use of the bytecode emitter for the busy functions. Signed-off-by: Damien George --- tests/thread/mutate_bytearray.py | 3 ++- tests/thread/mutate_dict.py | 3 ++- tests/thread/mutate_instance.py | 3 ++- tests/thread/mutate_list.py | 3 ++- tests/thread/mutate_set.py | 3 ++- tests/thread/stress_recurse.py | 3 ++- tests/thread/stress_schedule.py | 4 +++- tests/thread/thread_coop.py | 3 +++ tests/thread/thread_exc1.py | 3 ++- tests/thread/thread_gc1.py | 3 ++- tests/thread/thread_ident1.py | 3 ++- tests/thread/thread_lock3.py | 3 ++- tests/thread/thread_shared1.py | 3 ++- tests/thread/thread_shared2.py | 3 ++- tests/thread/thread_stacksize1.py | 3 ++- tests/thread/thread_stdin.py | 3 ++- 16 files changed, 34 insertions(+), 15 deletions(-) diff --git a/tests/thread/mutate_bytearray.py b/tests/thread/mutate_bytearray.py index b4664781a1..7116d291cf 100644 --- a/tests/thread/mutate_bytearray.py +++ b/tests/thread/mutate_bytearray.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared bytearray @@ -36,7 +37,7 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check bytearray has correct contents print(len(ba)) diff --git a/tests/thread/mutate_dict.py b/tests/thread/mutate_dict.py index 3777af6624..dd5f69e6c5 100644 --- a/tests/thread/mutate_dict.py +++ b/tests/thread/mutate_dict.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared dict @@ -38,7 +39,7 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check dict has correct contents print(sorted(di.items())) diff --git a/tests/thread/mutate_instance.py b/tests/thread/mutate_instance.py index 306ad91c95..63f7fb1e23 100644 --- a/tests/thread/mutate_instance.py +++ b/tests/thread/mutate_instance.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -40,7 +41,7 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check user instance has correct contents print(user.a, user.b, user.c) diff --git a/tests/thread/mutate_list.py b/tests/thread/mutate_list.py index 6f1e881254..d7398a2f1e 100644 --- a/tests/thread/mutate_list.py +++ b/tests/thread/mutate_list.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared list @@ -39,7 +40,7 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check list has correct contents li.sort() diff --git a/tests/thread/mutate_set.py b/tests/thread/mutate_set.py index 2d9a3e0ce9..7dcefa1d11 100644 --- a/tests/thread/mutate_set.py +++ b/tests/thread/mutate_set.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared set @@ -33,7 +34,7 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check set has correct contents print(sorted(se)) diff --git a/tests/thread/stress_recurse.py b/tests/thread/stress_recurse.py index 73b3a40f33..ec8b43fe8f 100644 --- a/tests/thread/stress_recurse.py +++ b/tests/thread/stress_recurse.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -24,5 +25,5 @@ _thread.start_new_thread(thread_entry, ()) # busy wait for thread to finish while not finished: - pass + time.sleep(0) print("done") diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index 97876f0f77..362d71aa12 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -27,6 +27,8 @@ def task(x): n += 1 +# This function must always use the bytecode emitter so it bounces the GIL when running. +@micropython.bytecode def thread(): while thread_run: try: @@ -46,7 +48,7 @@ for i in range(8): # Wait up to 10 seconds for 10000 tasks to be scheduled. t = time.ticks_ms() while n < _NUM_TASKS and time.ticks_diff(time.ticks_ms(), t) < _TIMEOUT_MS: - pass + time.sleep(0) # Stop all threads. thread_run = False diff --git a/tests/thread/thread_coop.py b/tests/thread/thread_coop.py index aefc4af074..85cda789c9 100644 --- a/tests/thread/thread_coop.py +++ b/tests/thread/thread_coop.py @@ -7,6 +7,7 @@ import _thread import sys from time import ticks_ms, ticks_diff, sleep_ms +import micropython done = False @@ -21,6 +22,8 @@ if sys.platform in ("win32", "linux", "darwin"): MAX_DELTA = 100 +# This function must always use the bytecode emitter so the VM can bounce the GIL when running. +@micropython.bytecode def busy_thread(): while not done: pass diff --git a/tests/thread/thread_exc1.py b/tests/thread/thread_exc1.py index cd87740929..cd6599983c 100644 --- a/tests/thread/thread_exc1.py +++ b/tests/thread/thread_exc1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -34,5 +35,5 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print("done") diff --git a/tests/thread/thread_gc1.py b/tests/thread/thread_gc1.py index b36ea9d4c8..45c17cc17b 100644 --- a/tests/thread/thread_gc1.py +++ b/tests/thread/thread_gc1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import gc import _thread @@ -44,6 +45,6 @@ n_thread += 1 # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(n_correct == n_finished) diff --git a/tests/thread/thread_ident1.py b/tests/thread/thread_ident1.py index 2a3732eff5..08cfd3eb36 100644 --- a/tests/thread/thread_ident1.py +++ b/tests/thread/thread_ident1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -27,6 +28,6 @@ print("main", type(tid_main) == int, tid_main != 0) new_tid = _thread.start_new_thread(thread_entry, ()) while not finished: - pass + time.sleep(0) print("done", type(new_tid) == int, new_tid == tid) diff --git a/tests/thread/thread_lock3.py b/tests/thread/thread_lock3.py index a927dc6829..c5acfa21b7 100644 --- a/tests/thread/thread_lock3.py +++ b/tests/thread/thread_lock3.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread lock = _thread.allocate_lock() @@ -26,4 +27,4 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) diff --git a/tests/thread/thread_shared1.py b/tests/thread/thread_shared1.py index 251e26fae6..c2e33abcec 100644 --- a/tests/thread/thread_shared1.py +++ b/tests/thread/thread_shared1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -40,5 +41,5 @@ n_thread += 1 # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(tup) diff --git a/tests/thread/thread_shared2.py b/tests/thread/thread_shared2.py index a1223c2b94..4ce9057ca0 100644 --- a/tests/thread/thread_shared2.py +++ b/tests/thread/thread_shared2.py @@ -3,6 +3,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -31,5 +32,5 @@ for i in range(n_thread): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(lst) diff --git a/tests/thread/thread_stacksize1.py b/tests/thread/thread_stacksize1.py index 140d165cb3..75e1da9642 100644 --- a/tests/thread/thread_stacksize1.py +++ b/tests/thread/thread_stacksize1.py @@ -3,6 +3,7 @@ # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import sys +import time import _thread # different implementations have different minimum sizes @@ -51,5 +52,5 @@ _thread.stack_size() # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print("done") diff --git a/tests/thread/thread_stdin.py b/tests/thread/thread_stdin.py index a469933f19..498b0a3a27 100644 --- a/tests/thread/thread_stdin.py +++ b/tests/thread/thread_stdin.py @@ -5,6 +5,7 @@ # This is a regression test for https://github.com/micropython/micropython/issues/15230 # on rp2, but doubles as a general property to test across all ports. import sys +import time import _thread try: @@ -38,7 +39,7 @@ StdinWaiter().wait_stdin(1000) # have run yet. The actual delay is <20ms but spinning here instead of # sleep(0.1) means the test can run on MP builds without float support. while not thread_waiter.is_done(): - pass + time.sleep(0) # The background thread should have completed its wait by now. print(thread_waiter.is_done())