py/gc: Execute finaliser code in a protected environment.

If a finaliser raises an exception then it must not propagate through the
GC sweep function.  This patch protects against such a thing by running
finaliser code via the mp_call_function_1_protected call.

This patch also adds scheduler lock/unlock calls around the finaliser
execution to further protect against any possible reentrancy issues: the
memory manager is already locked when doing a collection, but we also don't
want to allow any scheduled code to run, KeyboardInterrupts to interupt the
code, nor threads to switch.
This commit is contained in:
Damien George 2017-04-12 13:52:04 +10:00
parent 08242eed26
commit c7e8c6f7de

14
py/gc.c
View File

@ -258,18 +258,20 @@ STATIC void gc_sweep(void) {
case AT_HEAD:
#if MICROPY_ENABLE_FINALISER
if (FTB_GET(block)) {
#if MICROPY_PY_THREAD
// TODO need to think about reentrancy with finaliser code
assert(!"finaliser with threading not implemented");
#endif
mp_obj_base_t *obj = (mp_obj_base_t*)PTR_FROM_BLOCK(block);
if (obj->type != NULL) {
// if the object has a type then see if it has a __del__ method
mp_obj_t dest[2];
mp_load_method_maybe(MP_OBJ_FROM_PTR(obj), MP_QSTR___del__, dest);
if (dest[0] != MP_OBJ_NULL) {
// load_method returned a method
mp_call_method_n_kw(0, 0, dest);
// load_method returned a method, execute it in a protected environment
#if MICROPY_ENABLE_SCHEDULER
mp_sched_lock();
#endif
mp_call_function_1_protected(dest[0], dest[1]);
#if MICROPY_ENABLE_SCHEDULER
mp_sched_unlock();
#endif
}
}
// clear finaliser flag