-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
tests/run-tests.py: Auto detect whether the target has threading and the GIL or not. #17655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #17655 +/- ##
==========================================
- Coverage 98.44% 98.40% -0.04%
==========================================
Files 171 171
Lines 22192 22192
==========================================
- Hits 21847 21839 -8
- Misses 345 353 +8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code size report:
|
d63cbda
to
b90d11e
Compare
tests/thread/stress_aes.py
Outdated
try: | ||
|
||
def stackless(): | ||
pass | ||
|
||
micropython.heap_lock() | ||
stackless() | ||
micropython.heap_unlock() | ||
except RuntimeError: | ||
micropython.heap_unlock() | ||
is_stackless = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try: | |
def stackless(): | |
pass | |
micropython.heap_lock() | |
stackless() | |
micropython.heap_unlock() | |
except RuntimeError: | |
micropython.heap_unlock() | |
is_stackless = True | |
def stackless(): | |
pass | |
micropython.heap_lock() | |
try: | |
stackless() | |
except RuntimeError: | |
is_stackless = True | |
finally: | |
micropython.heap_unlock() |
does it work to make the try/except a bit more focused on where the actual exception is expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, it's probably better to be more focused like that. I will change it.
(I was just copying existing code for this from eg tests/micropython/heapalloc.py
.)
ports/unix/mpthreadport.c
Outdated
@@ -56,7 +56,7 @@ | |||
#endif | |||
|
|||
// This value seems to be about right for both 32-bit and 64-bit builds. | |||
#define THREAD_STACK_OVERFLOW_MARGIN (8192) | |||
#define THREAD_STACK_OVERFLOW_MARGIN (4 * 8192) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#define THREAD_STACK_OVERFLOW_MARGIN (4 * 8192) | |
#define THREAD_STACK_OVERFLOW_MARGIN (16 * 1024) |
Also, the commit message could use an explanation of why this is changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm currently trying to work out if this change is even needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is not needed, it doesn't fix the issue I thought it would fix. So I took it out.
tests/feature_check/thread_gil.py
Outdated
thread_done = True | ||
|
||
|
||
global thread_done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need global
statement at the top level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, now fixed.
tests/feature_check/thread_gil.py
Outdated
busy() | ||
while not thread_done: | ||
time.sleep(0) | ||
dt = time.ticks_diff(time.ticks_ms(), t0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depending on time measurements seems like it could be flaky, especially on desktop systems. I'm not awake enough yet though to try to come up with an alternative that doesn't involve creating a deadlock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it could be flaky. But from testing on bare-metal and PC, it looks reasonable. The timing is usually either very close to T or 2*T (depending if GIL is disable or enabled).
If it fails due to timing errors (running for too long), it will incorrectly think the GIL is enabled when it is not. Then it will run the mutate tests and they'll probably all fail. In such a case we can just rerun CI...
If it gets to be a real issue, could increase the timing here, eg double it (I just don't want the auto detection to take too long, because it's run at the start of each run of run-tests.py
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some similar concerns, and generally prefer the approach of explicitly adding this information in sys.implementation
or similar. Suggest we keep this approach now, but keep the explicit option in mind if it turns out to be flaky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having some information in sys.implementation
could actually be useful for users, instead of them having to run a tricky detection test like this (if they ever need to know the threading implementation...).
And I guess if you have threading enabled you have some amount of flash to spare (although not necessarily on cc3200 which does enable threading...).
So maybe it's worth adding sys.implementation._thread
after all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be useful to add. Especially if we don't add any _thread
property on builds without threading support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've now changed this GIL detection to use sys.implementation._thread
. It's a lot simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And retested on PICO_W, ESP32_GENERIC and unix port. They detect the GIL correctly and the relevant tests run.
Thanks @dlech for the review here. After I initially submitted this PR and all the CI failed on things I couldn't test locally, the scope of the PR increased quite a bit... so there was a lot of back and forth getting it to work. |
bf0c6cb
to
1266375
Compare
05a9ceb
to
28535b0
Compare
After a lot of back-and-forth with the CI, this PR is now in its final state. I updated the comment at the top with a better description of what this PR is aiming for. |
Signed-off-by: Damien George <damien@micropython.org>
This is a workaround for the case where threading is enabled without a GIL. In such a configuration, creating a new global variable is not atomic and threads have race conditions resizing/accessing the global dict. Signed-off-by: Damien George <damien@micropython.org>
Builds with stackless enabled are slower than non-stackless, and this test takes around 2 minutes on the unix port of MicroPython with the standard unix parameters. So detect stackless mode and reduce the time of the test. Signed-off-by: Damien George <damien@micropython.org>
When detecting the target platform, also check if it has threading and whether the GIL is enabled or not (using the new attribute `sys.implementation._thread`). If threading is available, add the thread tests to the set of tests to run (unless the set of tests is explicitly given). With this change, the unix port no longer needs to explicitly run the set of thread tests, so that line has been removed from the Makefile. This change will make sure thread tests are run with other testing combinations. In particular, thread tests are now run: - on the unix port with the native emitter - on macOS builds - on unix qemu, the architectures MIPS, ARM and RISCV-64 Signed-off-by: Damien George <damien@micropython.org>
The qemu emulation introduces enough overhead that the `tests/thread/stress_aes.py` test overruns the default timeout. So increase it to allow this test to pass. Signed-off-by: Damien George <damien@micropython.org>
This test passes sometimes and fails other times. Eventually that should be fixed, but for now just skip this test. Signed-off-by: Damien George <damien@micropython.org>
This test passes sometimes and fails other times. Eventually that should be fixed, but for now just skip this test. Signed-off-by: Damien George <damien@micropython.org>
28535b0
to
3c1c9ab
Compare
Summary
This PR makes the following changes to the test runner and CI:
_thread
module and, if so, add the threading tests to the set of tests to runtests/extmod/select_poll_eintr.py
testtests/thread/stress_aes.py
test do less work on stackless threading builds, so it finishes before the test timeout limitCurrently the information whether a port has the GIL is hard-coded: unix/PC targets and the rp2 port are considered to be GIL-less, and all other targets have the GIL enabled (if they have threading enabled). With the change here, some code is run to detect the GIL. That uses the fact that native code won't bounce the GIL and can hog it, and times how long code runs.
Overall the changes here mean that the threading tests are run in many more configurations:
Testing
Tested locally on the unix port, esp32 and rp2. Let's also see how the CI works.
Trade-offs and Alternatives
This uses a tricky technique to determine GIL/non-GIL. An alternative would be to add some indication in a function whether the GIL is enabled, eg
sys.implementation._thread_info
. But that will increase code size.