-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
py/malloc: Fix failed memory allocations by more aggressively freeing buffers. #16015
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
py/malloc: Fix failed memory allocations by more aggressively freeing buffers. #16015
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #16015 +/- ##
=======================================
Coverage 98.58% 98.59%
=======================================
Files 167 167
Lines 21590 21599 +9
=======================================
+ Hits 21285 21295 +10
+ Misses 305 304 -1 ☔ View full report in Codecov by Sentry. |
Code size report:
|
cfb5d5c
to
d2b8f2f
Compare
a6861ec
to
6ef0256
Compare
6ef0256
to
21a4e5a
Compare
21a4e5a
to
0048e7c
Compare
This looks really clean @projectgus ! Thanks for the thorough implementation and testing. That said, I can't help but think of the alternative where It could be tricky to implement though, eg it probably needs a recursive mutex for // free head and all of its tail blocks
do {
ATB_ANY_TO_FREE(area, block);
block += 1;
} while (ATB_GET_KIND(area, block) == AT_TAIL); That would be more of a self-contained fix, contained to |
I think I started out with the same idea, and came to a sticking point that I couldn't seem to resolve. Unfortunately I no longer remember what that sticking point was, but I'll take a quick look and get back to you. |
0048e7c
to
677bc67
Compare
This comment was marked as outdated.
This comment was marked as outdated.
a5aff04
to
630629e
Compare
ef241b3
to
732c047
Compare
Note for anyone reviewing: There are enough semi-related changes here that it might be worth reviewing as individual commits. |
732c047
to
90f3111
Compare
475d355
to
7a7bf31
Compare
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.
Looks good now, this has come a long way since its first version!
Please can you just modify the commit subject line for the two "gc: ..." commits to "py/gc: ..." for consistency (I still need to revisit #15823 for that).
5da2c06
to
c037c15
Compare
Done! |
Test is for an issue reported on the micropython-lib Discord as effecting the rp2 port umqtt.simple interface when reconnecting with TLS, however it's a more generic problem. Currently this test fails on RPI_PICO_W and ESP32_GENERIC_C3 (and no doubt others). Fixes are in the subsequent commits. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
On mbedTLS ports with non-baremetal configs (mostly esp32, technically also unix port), mbedTLS memory is allocated from the libc heap. This means an old SSL socket may be holding large SSL buffers and preventing a new SSL socket from being allocated. As a workaround, trigger a GC pass and retry before failing outright. This was originally implemented as a global mbedTLS calloc function, but there is complexity around the possibility of C user modules calling into mbedTLS without holding the GIL. It would be interesting to try making a generic version for any malloc which fails, but this would require checking for a Python thread and probably making the GIL recursive. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
This allows coverage to test MicroPython-specific features such as the tracked alloc cleanups added in the parent commit. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Currently a finalizer may run and access memory which has already been freed. (This happens mostly during gc_sweep_all() but could happen during any garbage collection pass.) Includes some speed improvement tweaks to skip empty FTB blocks. These help compensate for the inherent slowdown of having to walk the heap twice. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Do this by tracking being inside gc collection with a separate flag, GC_COLLECT_FLAG. In gc_free(), ignore this flag when determining if the heap is locked. * For finalisers calling gc_free() when heap is otherwise unlocked, this allows memory to be immediately freed (potentially avoiding a MemoryError). * Hard IRQs still can't call gc_free(), as heap will be locked via gc_lock(). * If finalisers are disabled then all of this code can be compiled out to save some code size. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Enabled by default if using threading and no GIL This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Necessary for GC support, also refactored pendsv usage. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Allows refactoring the existing thread_mutex atomic section support to use the new recursive mutex type. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
- Renamed gc_sweep to gc_sweep_free_blocks. - Call gc_sweep_run_finalisers from top level. - Reordered the gc static functions to be in approximate runtime sequence (with forward declarations) rather than in declaration order. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
c037c15
to
990f50f
Compare
Summary
This issue was originally reported on Discord by ondrej1024: on RPI_PICO_W a TLS connection via micropython-lib umqtt.simple would fail consistently on reconnect with
OSError(ENOMEM)
. A workaround was to explicitly close the old socket before connecting again. This doesn't seem like it should be necessary.Was able to generalise into the new test case in this PR:
extmod/ssl_noleak
. This fails when trying to allocate sequential SSL socket objects (one at a time) on rp2 and also other ports.Different fixes were needed, in separate commits in this PR. First approach was replaced with a new approach at suggestion of @dpgeorge, see inlinecomments below.
Changes:
gc_free()
during a GC sweep by tracking gc collection separately from "heap is locked".MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
. Have added an optional generic recursive mutex interface.Testing
extmod/ssl_noleak
fails consistently on master (using RPI_NANO_W and ESP32_GENERIC_C3 boards). Probably also fails on other ports usingMICROPY_MBEDTLS_CONFIG_BARE_METAL
, unless they have huge amounts of RAM.Performance
Performance benchmark run on PYB_V11 with all cache init commented out:
Trade-offs and Alternatives