-
Notifications
You must be signed in to change notification settings - Fork 220
Add threading implementation for the ESP32 #69
Conversation
ce7e3f6
to
422f413
Compare
extmod/moduselect.c
Outdated
MICROPY_EVENT_POLL_HOOK | ||
MP_THREAD_GIL_ENTER(); |
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.
Did you try putting the GIL exit/enter calls within the EVENT_POLL_HOOK macro itself? That's how stmhal's threading does it (see EVENT_POLL_HOOK in stmhal/mpconfigport.h).
#if MICROPY_PY_THREAD | ||
// TODO need to think about reentrancy with finaliser code | ||
assert(!"finaliser with threading not implemented"); | ||
#endif |
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 needs some thought. It has the same isses that wipy 1.0 originally had with interrupts and threading: when the GC runs it has the GIL, and so if a finaliser runs it also has the GIL. Then in the VM the finaliser may release the GIL which is not good because the GC is in the middle of running.
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.
OK... I'll leave that one to you. So far we don't allow any Python code to run within interrupts context, so we should be fine.
@@ -15,7 +15,8 @@ def thread_entry(n): | |||
_thread.start_new_thread(thread_entry, (thread_num,)) | |||
thread_num += 1 | |||
except MemoryError: | |||
pass | |||
# let other threads run | |||
time.sleep(0.05) |
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 shouldn't be necessary. I think the reason you originally needed it on your esp32 port was because in certain cases the VM would never release the GIL. This could happen if there was a short, infinite loop that had a try/except block which always raised an exception. This would reset the GIL divisor back to its maximum and therefore the counter would never reach zero and never release the GIL. This is fixed in the latest master.
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.
Ummm, I have tried in several different ways, and without the time.sleep()
line, it always gets stuck raising the memory error interrupt.
esp32/mpthreadport.c
Outdated
// move the start pointer | ||
thread = th->next; | ||
} | ||
// explicitely release all its memory |
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.
"explicitly" (no second "e")
esp32/mphalport.c
Outdated
} | ||
|
||
void mp_hal_delay_us(uint32_t us) { | ||
MP_THREAD_GIL_EXIT(); |
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.
For efficiency and accuracy I would not release the GIL during a microsecond sleep.
esp32/mphalport.c
Outdated
@@ -95,6 +103,7 @@ void mp_hal_delay_ms(uint32_t ms) { | |||
struct timeval tv_start; | |||
struct timeval tv_end; | |||
uint64_t dt; | |||
MP_THREAD_GIL_EXIT(); |
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.
If EVENT_POLL_HOOK does the release then this is not necessary.
esp32/mphalport.c
Outdated
@@ -44,12 +44,14 @@ ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array)}; | |||
|
|||
int mp_hal_stdin_rx_chr(void) { | |||
for (;;) { | |||
MP_THREAD_GIL_EXIT(); |
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 is not needed if EVENT_POLL_HOOK does the release.
@dpgeorge please check this.
Let other threads run while the select operation is blocking.
…run. We need this on the FreeRTOS ports to allow tasks with the same priority to share the cpu.
@dpgeorge I worked on your remarks (except 2 of them that need more thought). I also added an extra test to verify that threads are killed during a soft reset. |
@danicampora thanks! I will test the code and let you know about the 2 outstanding points (finalisers and the mod to the existing test). |
I tested the patch and it works pretty well. Some issues:
This seems to be an inherent limitation in the design of FreeRTOS and I see only one way to fix it: make all MicroPython tasks run at the idle priority (the lowest priority). This will probably affect overall performance. |
@danicampora to make progress here I suggest to merge the additions/changes to esp32/ and leave out the other things (tests, py changes). The latter are then things to fix at a later date (well, soon, but not right now). |
Umm OK. I tested this myself running several tests at once and it seemed to be fine. But you are right, we should use
OK, sounds good. |
@dpgeorge please merge it as you suggest. Thanks! |
Ok, this was merged in dc82def, f704375 and ff0ca1e Then I updated upstream to fix the thread+finaliser issue and merged that, see c7e8c6f The test for soft reset was not merged (see reason above). The one outstanding issue is that tests/thread/stress_create.py does not work because FreeRTOS does not reclaim deleted threads unless all remaining threads are idle. |
@dpgeorge awesome, thanks! |
This makes the ESP32 port pass all the threading tests, except for stress_aes.py which requires too much heap.
There's one change I'm not sure of: danicampora@d61fdf4
@dpgeorge is that assert still needed?