Skip to content

Commit a952fe1

Browse files
pi-anlandrewleech
authored andcommitted
py/modsys: Extend atexit to hold multiple functions.
Signed-off-by: Andrew Leech <[email protected]>
1 parent 838f212 commit a952fe1

File tree

5 files changed

+44
-5
lines changed

5 files changed

+44
-5
lines changed

py/modsys.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,42 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof);
208208
#endif
209209

210210
#if MICROPY_PY_SYS_ATEXIT
211+
typedef struct _m_atexit_node_t {
212+
struct _m_atexit_node_t *prev;
213+
struct _m_atexit_node_t *next;
214+
mp_obj_t fn;
215+
} m_atexit_node_t;
216+
211217
// atexit(callback): Callback is called when sys.exit is called.
212218
static mp_obj_t mp_sys_atexit(mp_obj_t obj) {
213-
mp_obj_t old = MP_STATE_VM(sys_exitfunc);
214-
MP_STATE_VM(sys_exitfunc) = obj;
215-
return old;
219+
m_atexit_node_t *node = m_malloc(sizeof(m_atexit_node_t));
220+
if (MP_STATE_VM(sys_exitfunc) != mp_const_none) {
221+
MP_STATE_VM(sys_exitfunc)->prev = node;
222+
}
223+
node->fn = obj;
224+
node->prev = NULL;
225+
node->next = MP_STATE_VM(sys_exitfunc);
226+
MP_STATE_VM(sys_exitfunc) = node;
227+
return obj;
216228
}
217229
static MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_atexit_obj, mp_sys_atexit);
230+
231+
void mp_sys_atexit_execute(void) {
232+
// walk through the linked list and execute each function
233+
// Beware, the sys.settrace callback should be disabled before running sys.atexit.
234+
while (MP_STATE_VM(sys_exitfunc) != mp_const_none) {
235+
if (mp_obj_is_callable(MP_STATE_VM(sys_exitfunc)->fn)) {
236+
nlr_buf_t nlr;
237+
if (nlr_push(&nlr) == 0) {
238+
mp_call_function_0(MP_STATE_VM(sys_exitfunc)->fn);
239+
} else {
240+
// Uncaught exception: print it out.
241+
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
242+
}
243+
}
244+
MP_STATE_VM(sys_exitfunc) = MP_STATE_VM(sys_exitfunc)->next;
245+
}
246+
}
218247
#endif
219248

220249
#if MICROPY_PY_SYS_SETTRACE
@@ -364,7 +393,7 @@ MP_REGISTER_ROOT_POINTER(mp_obj_base_t * cur_exception);
364393

365394
#if MICROPY_PY_SYS_ATEXIT
366395
// exposed through sys.atexit function
367-
MP_REGISTER_ROOT_POINTER(mp_obj_t sys_exitfunc);
396+
MP_REGISTER_ROOT_POINTER(struct _m_atexit_node_t *sys_exitfunc);
368397
#endif
369398

370399
#if MICROPY_PY_SYS_ATTR_DELEGATION

py/mpconfig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,7 @@ typedef double mp_float_t;
14881488

14891489
// Whether to provide "sys.atexit" function (MicroPython extension)
14901490
#ifndef MICROPY_PY_SYS_ATEXIT
1491-
#define MICROPY_PY_SYS_ATEXIT (0)
1491+
#define MICROPY_PY_SYS_ATEXIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
14921492
#endif
14931493

14941494
// Whether to provide the "sys.path" attribute (which forces module delegation

py/runtime.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ extern const byte mp_binary_op_method_name[];
9595
void mp_init(void);
9696
void mp_deinit(void);
9797

98+
#if MICROPY_PY_SYS_ATEXIT
99+
void mp_sys_atexit_execute(void);
100+
#endif
101+
98102
void mp_sched_exception(mp_obj_t exc);
99103
void mp_sched_keyboard_interrupt(void);
100104
#if MICROPY_ENABLE_VM_ABORT

tests/misc/sys_atexit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ def do_at_exit():
1515
print("done at exit:", some_var)
1616

1717

18+
@sys.atexit
19+
def do_at_exit_2():
20+
print("done at exit last")
21+
22+
1823
sys.atexit(do_at_exit)
1924

2025
some_var = "ok"

tests/misc/sys_atexit.py.exp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
done before exit
22
done at exit: ok
3+
done at exit last

0 commit comments

Comments
 (0)