Skip to content

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

Merged
merged 9 commits into from
Feb 3, 2025
15 changes: 15 additions & 0 deletions extmod/modtls_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "py/stream.h"
#include "py/objstr.h"
#include "py/reader.h"
#include "py/gc.h"
#include "extmod/vfs.h"

// mbedtls_time_t
Expand All @@ -58,6 +59,10 @@
#include "mbedtls/asn1.h"
#endif

#ifndef MICROPY_MBEDTLS_CONFIG_BARE_METAL
#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (0)
#endif

#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)

// This corresponds to an SSLContext object.
Expand Down Expand Up @@ -545,6 +550,16 @@ static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
mbedtls_ssl_init(&o->ssl);

ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf);
#if !MICROPY_MBEDTLS_CONFIG_BARE_METAL
if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) {
// If mbedTLS relies on platform libc heap for buffers (i.e. esp32
// port), then run a GC pass and then try again. This is useful because
// it may free a Python object (like an old SSL socket) whose finaliser
// frees some platform-level heap.
gc_collect();
ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf);
}
#endif
if (ret != 0) {
goto cleanup;
}
Expand Down
4 changes: 2 additions & 2 deletions ports/rp2/mpthreadport.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ void mp_thread_deinit(void) {
assert(get_core_num() == 0);
// Must ensure that core1 is not currently holding the GC lock, otherwise
// it will be terminated while holding the lock.
mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1);
mp_thread_recursive_mutex_lock(&MP_STATE_MEM(gc_mutex), 1);
multicore_reset_core1();
core1_entry = NULL;
mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex));
mp_thread_recursive_mutex_unlock(&MP_STATE_MEM(gc_mutex));
}

void mp_thread_gc_others(void) {
Expand Down
20 changes: 19 additions & 1 deletion ports/rp2/mpthreadport.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
#ifndef MICROPY_INCLUDED_RP2_MPTHREADPORT_H
#define MICROPY_INCLUDED_RP2_MPTHREADPORT_H

#include "pico/mutex.h"
#include "mutex_extra.h"

typedef struct mutex mp_thread_mutex_t;
typedef recursive_mutex_nowait_t mp_thread_recursive_mutex_t;

extern void *core_state[2];

Expand Down Expand Up @@ -65,4 +66,21 @@ static inline void mp_thread_mutex_unlock(mp_thread_mutex_t *m) {
mutex_exit(m);
}

static inline void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *m) {
recursive_mutex_nowait_init(m);
}

static inline int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *m, int wait) {
if (wait) {
recursive_mutex_nowait_enter_blocking(m);
return 1;
} else {
return recursive_mutex_nowait_try_enter(m, NULL);
}
}

static inline void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *m) {
recursive_mutex_nowait_exit(m);
}

#endif // MICROPY_INCLUDED_RP2_MPTHREADPORT_H
14 changes: 7 additions & 7 deletions ports/rp2/pendsv.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#include <assert.h>
#include "py/mpconfig.h"
#include "mutex_extra.h"
#include "py/mpthread.h"
#include "pendsv.h"

#if PICO_RP2040
Expand All @@ -47,21 +47,21 @@ void PendSV_Handler(void);

// Using the nowait variant here as softtimer updates PendSV from the loop of mp_wfe_or_timeout(),
// where we don't want the CPU event bit to be set.
static recursive_mutex_nowait_t pendsv_mutex;
static mp_thread_recursive_mutex_t pendsv_mutex;

void pendsv_init(void) {
recursive_mutex_nowait_init(&pendsv_mutex);
mp_thread_recursive_mutex_init(&pendsv_mutex);
}

void pendsv_suspend(void) {
// Recursive Mutex here as either core may call pendsv_suspend() and expect
// both mutual exclusion (other core can't enter pendsv_suspend() at the
// same time), and that no PendSV handler will run.
recursive_mutex_nowait_enter_blocking(&pendsv_mutex);
mp_thread_recursive_mutex_lock(&pendsv_mutex, 1);
}

void pendsv_resume(void) {
recursive_mutex_nowait_exit(&pendsv_mutex);
mp_thread_recursive_mutex_unlock(&pendsv_mutex);

// Run pendsv if needed. Find an entry with a dispatch and call pendsv dispatch
// with it. If pendsv runs it will service all slots.
Expand Down Expand Up @@ -97,7 +97,7 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) {
// PendSV interrupt handler to perform background processing.
void PendSV_Handler(void) {

if (!recursive_mutex_nowait_try_enter(&pendsv_mutex, NULL)) {
if (!mp_thread_recursive_mutex_lock(&pendsv_mutex, 0)) {
// Failure here means core 1 holds pendsv_mutex. ISR will
// run again after core 1 calls pendsv_resume().
return;
Expand All @@ -117,5 +117,5 @@ void PendSV_Handler(void) {
}
}

recursive_mutex_nowait_exit(&pendsv_mutex);
mp_thread_recursive_mutex_unlock(&pendsv_mutex);
}
11 changes: 11 additions & 0 deletions ports/unix/mbedtls/mbedtls_config_port.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,18 @@
// Enable mbedtls modules
#define MBEDTLS_TIMING_C

#if defined(MICROPY_UNIX_COVERAGE)
// Test the "bare metal" memory management in the coverage build
#define MICROPY_MBEDTLS_CONFIG_BARE_METAL (1)
#endif

// Include common mbedtls configuration.
#include "extmod/mbedtls/mbedtls_config_common.h"

#if defined(MICROPY_UNIX_COVERAGE)
// See comment above, but fall back to the default platform entropy functions
#undef MBEDTLS_ENTROPY_HARDWARE_ALT
#undef MBEDTLS_NO_PLATFORM_ENTROPY
#endif

#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */
31 changes: 24 additions & 7 deletions ports/unix/mpthreadport.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ static pthread_key_t tls_key;
// The mutex is used for any code in this port that needs to be thread safe.
// Specifically for thread management, access to the linked list is one example.
// But also, e.g. scheduler state.
static pthread_mutex_t thread_mutex;
static mp_thread_recursive_mutex_t thread_mutex;
static mp_thread_t *thread;

// this is used to synchronise the signal handler of the thread
Expand All @@ -78,11 +78,11 @@ static sem_t thread_signal_done;
#endif

void mp_thread_unix_begin_atomic_section(void) {
pthread_mutex_lock(&thread_mutex);
mp_thread_recursive_mutex_lock(&thread_mutex, true);
}

void mp_thread_unix_end_atomic_section(void) {
pthread_mutex_unlock(&thread_mutex);
mp_thread_recursive_mutex_unlock(&thread_mutex);
}

// this signal handler is used to scan the regs and stack of a thread
Expand Down Expand Up @@ -113,10 +113,7 @@ void mp_thread_init(void) {

// Needs to be a recursive mutex to emulate the behavior of
// BEGIN_ATOMIC_SECTION on bare metal.
pthread_mutexattr_t thread_mutex_attr;
pthread_mutexattr_init(&thread_mutex_attr);
pthread_mutexattr_settype(&thread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&thread_mutex, &thread_mutex_attr);
mp_thread_recursive_mutex_init(&thread_mutex);

// create first entry in linked list of all threads
thread = malloc(sizeof(mp_thread_t));
Expand Down Expand Up @@ -321,6 +318,26 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
// TODO check return value
}

#if MICROPY_PY_THREAD_RECURSIVE_MUTEX

void mp_thread_recursive_mutex_init(mp_thread_recursive_mutex_t *mutex) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(mutex, &attr);
pthread_mutexattr_destroy(&attr);
}

int mp_thread_recursive_mutex_lock(mp_thread_recursive_mutex_t *mutex, int wait) {
return mp_thread_mutex_lock(mutex, wait);
}

void mp_thread_recursive_mutex_unlock(mp_thread_recursive_mutex_t *mutex) {
mp_thread_mutex_unlock(mutex);
}

#endif // MICROPY_PY_THREAD_RECURSIVE_MUTEX

#endif // MICROPY_PY_THREAD

// this is used even when MICROPY_PY_THREAD is disabled
Expand Down
1 change: 1 addition & 0 deletions ports/unix/mpthreadport.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <stdbool.h>

typedef pthread_mutex_t mp_thread_mutex_t;
typedef pthread_mutex_t mp_thread_recursive_mutex_t;

void mp_thread_init(void);
void mp_thread_deinit(void);
Expand Down
Loading
Loading