Skip to content

Commit 2265d70

Browse files
committed
tests/thread: Adjust thread tests so most are able to run on rp2 port.
The aim of this commit is to make it so that the existing thread tests can be used to test the _thread module on the rp2 port. The rp2 port only allows up to one thread to be created at a time, and does not have the GIL enabled. The following changes have been made: - run-tests.py skips mutation tests on rp2, because there's no GIL. - run-tests.py skips other tests on rp2 that require more than one thread. - The tests stop trying to start a new thread after there is an OSError, which indicates that the system cannot create more threads. - Some of these tests also now run the test function on the main thread, not just the spawned threads. - In some tests the output printing is adjusted so it's the same regardless of how many threads were spawned. - Some time.sleep(1) are replaced with time.sleep(0) to make the tests run a little faster (finish sooner when the work is done). For the most part the tests are unchanged for existing platforms like esp32 and unix. Signed-off-by: Damien George <damien@micropython.org>
1 parent 231fc20 commit 2265d70

8 files changed

Lines changed: 93 additions & 26 deletions

File tree

tests/run-tests.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,13 +562,19 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
562562
skip_tests.add("cmdline/repl_sys_ps1_ps2.py")
563563
skip_tests.add("extmod/ssl_poll.py")
564564

565-
# Some tests shouldn't be run on a PC
566-
if args.target == "unix":
567-
# unix build does not have the GIL so can't run thread mutation tests
565+
# Skip thread mutation tests on targets that don't have the GIL.
566+
if args.target in ("rp2", "unix"):
568567
for t in tests:
569568
if t.startswith("thread/mutate_"):
570569
skip_tests.add(t)
571570

571+
# Skip thread tests that require many threads on targets that don't support multiple threads.
572+
if args.target == "rp2":
573+
skip_tests.add("thread/stress_heap.py")
574+
skip_tests.add("thread/thread_lock2.py")
575+
skip_tests.add("thread/thread_lock3.py")
576+
skip_tests.add("thread/thread_shared2.py")
577+
572578
# Some tests shouldn't be run on pyboard
573579
if args.target != "unix":
574580
skip_tests.add("basics/exception_chain.py") # warning is not printed
@@ -987,7 +993,7 @@ def main():
987993
elif args.target in ("renesas-ra"):
988994
test_dirs += ("float", "inlineasm", "renesas-ra")
989995
elif args.target == "rp2":
990-
test_dirs += ("float", "stress", "inlineasm")
996+
test_dirs += ("float", "stress", "inlineasm", "thread")
991997
elif args.target in ("esp8266", "esp32", "minimal", "nrf"):
992998
test_dirs += ("float",)
993999
elif args.target == "wipy":

tests/thread/stress_schedule.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
n = 0 # How many times the task successfully ran.
2121
t = None # Start time of test, assigned here to preallocate entry in globals dict.
22+
thread_run = True # If the thread should continue running.
2223

2324

2425
def task(x):
@@ -27,7 +28,7 @@ def task(x):
2728

2829

2930
def thread():
30-
while True:
31+
while thread_run:
3132
try:
3233
micropython.schedule(task, None)
3334
except RuntimeError:
@@ -36,13 +37,21 @@ def thread():
3637

3738

3839
for i in range(8):
39-
_thread.start_new_thread(thread, ())
40+
try:
41+
_thread.start_new_thread(thread, ())
42+
except OSError:
43+
# System cannot create a new thead, so stop trying to create them.
44+
break
4045

4146
# Wait up to 10 seconds for 10000 tasks to be scheduled.
4247
t = time.ticks_ms()
4348
while n < _NUM_TASKS and time.ticks_diff(time.ticks_ms(), t) < _TIMEOUT_MS:
4449
pass
4550

51+
# Stop all threads.
52+
thread_run = False
53+
time.sleep_ms(20)
54+
4655
if n < _NUM_TASKS:
4756
# Not all the tasks were scheduled, likely the scheduler stopped working.
4857
print(n)

tests/thread/thread_gc1.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,34 @@ def thread_entry(n):
1616
data[i] = data[i]
1717
gc.collect()
1818

19-
# print whether the data remains intact and indicate we are finished
19+
# check whether the data remains intact and indicate we are finished
2020
with lock:
21-
print(list(data) == list(range(256)))
22-
global n_finished
21+
global n_correct, n_finished
22+
n_correct += list(data) == list(range(256))
2323
n_finished += 1
2424

2525

2626
lock = _thread.allocate_lock()
27-
n_thread = 4
27+
n_thread = 0
28+
n_thread_max = 4
29+
n_correct = 0
2830
n_finished = 0
2931

3032
# spawn threads
31-
for i in range(n_thread):
32-
_thread.start_new_thread(thread_entry, (10,))
33+
for _ in range(n_thread_max):
34+
try:
35+
_thread.start_new_thread(thread_entry, (10,))
36+
n_thread += 1
37+
except OSError:
38+
# System cannot create a new thead, so stop trying to create them.
39+
break
40+
41+
# also run the function on this main thread
42+
thread_entry(10)
43+
n_thread += 1
3344

3445
# busy wait for threads to finish
3546
while n_finished < n_thread:
3647
pass
48+
49+
print(n_correct == n_finished)

tests/thread/thread_ident1.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
import _thread
66

77

8+
# Initialise variables (also preallocate their spot in the globals dict so the
9+
# globals dict is not resized while threads are running).
810
tid = None
11+
tid_main = None
12+
new_tid = None
13+
finished = False
914

1015

1116
def thread_entry():
@@ -19,7 +24,6 @@ def thread_entry():
1924
tid_main = _thread.get_ident()
2025
print("main", type(tid_main) == int, tid_main != 0)
2126

22-
finished = False
2327
new_tid = _thread.start_new_thread(thread_entry, ())
2428

2529
while not finished:

tests/thread/thread_lock4.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,18 @@ def thread_entry():
3636

3737
# spawn threads to do the jobs
3838
for i in range(4):
39-
_thread.start_new_thread(thread_entry, ())
39+
try:
40+
_thread.start_new_thread(thread_entry, ())
41+
except OSError:
42+
# System cannot create a new thead, so stop trying to create them.
43+
break
4044

4145
# wait for the jobs to complete
4246
while True:
4347
with jobs_lock:
4448
if len(output) == n_jobs:
4549
break
46-
time.sleep(1)
50+
time.sleep(0)
4751

4852
# sort and print the results
4953
output.sort(key=lambda x: x[0])

tests/thread/thread_qstr1.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,26 @@ def th(base, n):
2424

2525

2626
lock = _thread.allocate_lock()
27-
n_thread = 4
27+
n_thread = 0
28+
n_thread_max = 4
2829
n_finished = 0
2930
n_qstr_per_thread = 100 # make 1000 for a more stressful test (uses more heap)
3031

3132
# spawn threads
32-
for i in range(n_thread):
33-
_thread.start_new_thread(th, (i * n_qstr_per_thread, n_qstr_per_thread))
33+
for _ in range(n_thread_max):
34+
try:
35+
_thread.start_new_thread(th, (n_thread * n_qstr_per_thread, n_qstr_per_thread))
36+
n_thread += 1
37+
except OSError:
38+
# System cannot create a new thead, so stop trying to create them.
39+
break
40+
41+
# also run the function on this main thread
42+
th(n_thread * n_qstr_per_thread, n_qstr_per_thread)
43+
n_thread += 1
3444

3545
# wait for threads to finish
3646
while n_finished < n_thread:
37-
time.sleep(1)
47+
time.sleep(0)
3848

3949
print("pass")

tests/thread/thread_shared1.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,25 @@ def thread_entry(n, tup):
1818

1919

2020
lock = _thread.allocate_lock()
21-
n_thread = 2
21+
n_thread = 0
22+
n_thread_max = 2
2223
n_finished = 0
2324

2425
# the shared data structure
2526
tup = (1, 2, 3, 4)
2627

2728
# spawn threads
28-
for i in range(n_thread):
29-
_thread.start_new_thread(thread_entry, (100, tup))
29+
for _ in range(n_thread_max):
30+
try:
31+
_thread.start_new_thread(thread_entry, (100, tup))
32+
n_thread += 1
33+
except OSError:
34+
# System cannot create a new thead, so stop trying to create them.
35+
break
36+
37+
# also run the function on this main thread
38+
thread_entry(100, tup)
39+
n_thread += 1
3040

3141
# busy wait for threads to finish
3242
while n_finished < n_thread:

tests/thread/thread_sleep1.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
import _thread
1313

1414
lock = _thread.allocate_lock()
15-
n_thread = 4
15+
n_thread = 0
16+
n_thread_max = 4
1617
n_finished = 0
1718

1819

@@ -24,10 +25,20 @@ def thread_entry(t):
2425
n_finished += 1
2526

2627

27-
for i in range(n_thread):
28-
_thread.start_new_thread(thread_entry, (10 * i,))
28+
# spawn threads
29+
for _ in range(n_thread_max):
30+
try:
31+
_thread.start_new_thread(thread_entry, (10 * n_thread,))
32+
n_thread += 1
33+
except OSError:
34+
# System cannot create a new thead, so stop trying to create them.
35+
break
36+
37+
# also run the function on this main thread
38+
thread_entry(10 * n_thread)
39+
n_thread += 1
2940

3041
# wait for threads to finish
3142
while n_finished < n_thread:
3243
sleep_ms(100)
33-
print("done", n_thread)
44+
print("done")

0 commit comments

Comments
 (0)