Skip to content

Commit f0954e3

Browse files
committed
py: Add emergency exception object for when heap allocation fails.
1 parent 6f355fd commit f0954e3

File tree

3 files changed

+54
-20
lines changed

3 files changed

+54
-20
lines changed

py/malloc.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ void *m_malloc(int num_bytes) {
5353
return ptr;
5454
}
5555

56+
void *m_malloc_maybe(int num_bytes) {
57+
void *ptr = malloc(num_bytes);
58+
if (ptr == NULL) {
59+
return NULL;
60+
}
61+
#if MICROPY_MEM_STATS
62+
total_bytes_allocated += num_bytes;
63+
current_bytes_allocated += num_bytes;
64+
UPDATE_PEAK();
65+
#endif
66+
DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
67+
return ptr;
68+
}
69+
5670
#if MICROPY_ENABLE_FINALISER
5771
void *m_malloc_with_finaliser(int num_bytes) {
5872
if (num_bytes == 0) {

py/misc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ typedef unsigned int uint;
3838
#define m_del_var(obj_type, var_type, var_num, ptr) (m_free(ptr, sizeof(obj_type) + sizeof(var_type) * (var_num)))
3939

4040
void *m_malloc(int num_bytes);
41+
void *m_malloc_maybe(int num_bytes);
4142
void *m_malloc_with_finaliser(int num_bytes);
4243
void *m_malloc0(int num_bytes);
4344
void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes);

py/objexcept.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ typedef struct _mp_obj_exception_t {
2020
// Instance of MemoryError exception - needed by mp_malloc_fail
2121
const mp_obj_exception_t mp_const_MemoryError_obj = {{&mp_type_MemoryError}, MP_OBJ_NULL, {{&mp_type_tuple}, 0}};
2222

23+
// Local non-heap memory for allocating an exception when we run out of RAM
24+
STATIC mp_obj_exception_t mp_emergency_exception_obj;
25+
2326
// Instance of GeneratorExit exception - needed by generator.close()
2427
// This would belong to objgenerator.c, but to keep mp_obj_exception_t
2528
// definition module-private so far, have it here.
@@ -51,7 +54,13 @@ STATIC mp_obj_t mp_obj_exception_make_new(mp_obj_t type_in, uint n_args, uint n_
5154
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "%s does not take keyword arguments", mp_obj_get_type_str(type_in)));
5255
}
5356

54-
mp_obj_exception_t *o = m_new_obj_var(mp_obj_exception_t, mp_obj_t, n_args);
57+
mp_obj_exception_t *o = m_malloc_maybe(sizeof(mp_obj_exception_t) + n_args * sizeof(mp_obj_t));
58+
if (o == NULL) {
59+
// Couldn't allocate heap memory; use local data instead.
60+
o = &mp_emergency_exception_obj;
61+
// We can't store any args.
62+
n_args = 0;
63+
}
5564
o->base.type = type;
5665
o->traceback = MP_OBJ_NULL;
5766
o->args.base.type = &mp_type_tuple;
@@ -196,25 +205,35 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char
196205
assert(exc_type->make_new == mp_obj_exception_make_new);
197206

198207
// make exception object
199-
mp_obj_exception_t *o = m_new_obj_var(mp_obj_exception_t, mp_obj_t, 1);
200-
o->base.type = exc_type;
201-
o->traceback = MP_OBJ_NULL;
202-
o->args.base.type = &mp_type_tuple;
203-
o->args.len = 1;
204-
205-
if (fmt == NULL) {
206-
// no message
207-
assert(0);
208+
mp_obj_exception_t *o = m_malloc_maybe(sizeof(mp_obj_exception_t) + 1 * sizeof(mp_obj_t));
209+
if (o == NULL) {
210+
// Couldn't allocate heap memory; use local data instead.
211+
// Unfortunately, we won't be able to format the string...
212+
o = &mp_emergency_exception_obj;
213+
o->base.type = exc_type;
214+
o->traceback = MP_OBJ_NULL;
215+
o->args.base.type = &mp_type_tuple;
216+
o->args.len = 0;
208217
} else {
209-
// render exception message and store as .args[0]
210-
// TODO: optimize bufferbloat
211-
vstr_t *vstr = vstr_new();
212-
va_list ap;
213-
va_start(ap, fmt);
214-
vstr_vprintf(vstr, fmt, ap);
215-
va_end(ap);
216-
o->args.items[0] = mp_obj_new_str((byte*)vstr->buf, vstr->len, false);
217-
vstr_free(vstr);
218+
o->base.type = exc_type;
219+
o->traceback = MP_OBJ_NULL;
220+
o->args.base.type = &mp_type_tuple;
221+
o->args.len = 1;
222+
223+
if (fmt == NULL) {
224+
// no message
225+
assert(0);
226+
} else {
227+
// render exception message and store as .args[0]
228+
// TODO: optimize bufferbloat
229+
vstr_t *vstr = vstr_new();
230+
va_list ap;
231+
va_start(ap, fmt);
232+
vstr_vprintf(vstr, fmt, ap);
233+
va_end(ap);
234+
o->args.items[0] = mp_obj_new_str((byte*)vstr->buf, vstr->len, false);
235+
vstr_free(vstr);
236+
}
218237
}
219238

220239
return o;
@@ -259,7 +278,7 @@ void mp_obj_exception_clear_traceback(mp_obj_t self_in) {
259278
void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, machine_uint_t line, qstr block) {
260279
// make sure self_in is an exception instance
261280
// TODO add traceback information to user-defined exceptions (need proper builtin subclassing for that)
262-
if (mp_obj_get_type(self_in)->make_new == mp_obj_exception_make_new) {
281+
if (mp_obj_get_type(self_in)->make_new == mp_obj_exception_make_new && self_in != &mp_emergency_exception_obj) {
263282
mp_obj_exception_t *self = self_in;
264283

265284
// for traceback, we are just using the list object for convenience, it's not really a list of Python objects

0 commit comments

Comments
 (0)