From e522e8d262c0a1b692bb23f43549fb4090fcdebb Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Tue, 17 Dec 2024 13:46:46 +0100 Subject: [PATCH] bl00mbox: fix weakref behavior --- components/bl00mbox/bl00mbox_audio.c | 12 +++- components/bl00mbox/include/bl00mbox_audio.h | 1 + .../bl00mbox/micropython/bl00mbox/_user.py | 28 +++++--- .../bl00mbox/micropython/mp_sys_bl00mbox.c | 65 ++++++++++--------- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/components/bl00mbox/bl00mbox_audio.c b/components/bl00mbox/bl00mbox_audio.c index 9137ffed39..d5d79ce704 100644 --- a/components/bl00mbox/bl00mbox_audio.c +++ b/components/bl00mbox/bl00mbox_audio.c @@ -175,10 +175,20 @@ void bl00mbox_channel_destroy(bl00mbox_channel_t * chan){ // remove from parent if(* (chan->parent_self_ref) != chan){ - bl00mbox_log_error("channel: parent_self_ref improper: channel %p, ref %p", chan, * (chan->parent_self_ref)); + bl00mbox_log_error("channel: parent_self_ref improper: channel %p, ref %p", + chan, * (chan->parent_self_ref)); } * (chan->parent_self_ref) = NULL; + // remove from weak parent + if(chan->weak_parent_self_ref){ + if(* (chan->weak_parent_self_ref) != chan){ + bl00mbox_log_error("channel: weak_parent_self_ref improper: channel %p, ref %p", + chan, * (chan->weak_parent_self_ref)); + } + * (chan->weak_parent_self_ref) = NULL; + } + #ifdef BL00MBOX_DEBUG if(chan->connections.len) bl00mbox_log_error("connections nonempty"); if(chan->roots.len) bl00mbox_log_error("roots nonempty"); diff --git a/components/bl00mbox/include/bl00mbox_audio.h b/components/bl00mbox/include/bl00mbox_audio.h index b23af0aaa9..41552c44a1 100644 --- a/components/bl00mbox/include/bl00mbox_audio.h +++ b/components/bl00mbox/include/bl00mbox_audio.h @@ -89,6 +89,7 @@ typedef struct _bl00mbox_channel_t{ void * parent; struct _bl00mbox_channel_t ** parent_self_ref; + struct _bl00mbox_channel_t ** weak_parent_self_ref; } bl00mbox_channel_t; void bl00mbox_audio_init(); diff --git a/components/bl00mbox/micropython/bl00mbox/_user.py b/components/bl00mbox/micropython/bl00mbox/_user.py index 893a69048d..2f99bca564 100644 --- a/components/bl00mbox/micropython/bl00mbox/_user.py +++ b/components/bl00mbox/micropython/bl00mbox/_user.py @@ -446,9 +446,9 @@ class Channel: def __init__(self, name="repl"): # never ever hand out the deep core to somebody else to make sure channel gets gc'd right self._deep_core = sys_bl00mbox.ChannelCore(name) - self._core = self._deep_core.make_fake() + self._core = self._deep_core.get_weak() # also don't hand out self, use this one instead - self._fake_channel = FakeChannel(self._core) + self._weak_channel = WeakChannel(self._core) if _channel_init_callback is not None: _channel_init_callback(self) self._channel_plugin = bl00mbox._plugins._get_plugin(self._core.channel_plugin) @@ -484,7 +484,7 @@ class Channel: def _new_plugin(self, thing, *args, **kwargs): if isinstance(thing, bl00mbox._plugins._PluginDescriptor): return bl00mbox._plugins._make_plugin( - self._fake_channel, thing.plugin_id, *args, **kwargs + self._weak_channel, thing.plugin_id, *args, **kwargs ) else: raise TypeError("not a plugin") @@ -493,7 +493,7 @@ class Channel: self.free = False if type(thing) == type: if issubclass(thing, bl00mbox.patches._Patch): - return thing(self._fake_channel, *args, **kwargs) + return thing(self._weak_channel, *args, **kwargs) elif isinstance(thing, bl00mbox._plugins._PluginDescriptor) or ( type(thing) == int ): @@ -556,14 +556,20 @@ class Channel: raise Bl00mboxError("can't connect this") -class FakeChannel(Channel): +class WeakChannel(Channel): def __init__(self, core): - self._core = core + self._core = core.get_weak() self._channel_plugin = bl00mbox._plugins._get_plugin(self._core.channel_plugin) - self._fake_channel = self + self._weak_channel = self + +class SysChannel(Channel): + def __init__(self, core): + self._deep_core = core + self._core = core.get_weak() + self._channel_plugin = bl00mbox._plugins._get_plugin(self._core.channel_plugin) + self._weak_channel = WeakChannel(self._core) -class SysChannel(FakeChannel): @property def sys_gain_dB(self): ret = self._core.sys_gain @@ -615,10 +621,12 @@ class Sys: core.foreground = False @staticmethod - def collect_channels(active): + def collect_channels(active, weak_ref=False): cores = sys_bl00mbox.collect_channels(active) for core in cores: - yield SysChannel(core.make_fake()) + if weak_ref: + core = core.get_weak() + yield SysChannel(core) @staticmethod def collect_active_callbacks(): diff --git a/components/bl00mbox/micropython/mp_sys_bl00mbox.c b/components/bl00mbox/micropython/mp_sys_bl00mbox.c index e1b8a5450f..441d02cb95 100644 --- a/components/bl00mbox/micropython/mp_sys_bl00mbox.c +++ b/components/bl00mbox/micropython/mp_sys_bl00mbox.c @@ -18,16 +18,20 @@ MP_DEFINE_EXCEPTION(ReferenceError, Exception) typedef struct _channel_core_obj_t { mp_obj_base_t base; - // a fake reference never deletes the channel - bool fake; - // points to chan of original. dereferenced value might be NULL if - // channel has been manually deleted, use channel_core_verify() - bl00mbox_channel_t **chan_ref; // will be NULLed on original if channel has been manually deleted + // both for weak and regular bl00mbox_channel_t *chan; + + // pointer to object that weakly references original channel object + // there is no more than one weak channel reference obj per channel + mp_obj_t weak; + + // things we don't want garbage collected: + // - channel plugin mp_obj_t channel_plugin; - // list of all sources connect to the mixer to avoid gc + // - list of all sources connect to the mixer mp_obj_t sources_mx; + #ifdef BL00MBOX_CALLBACKS_FUNSAFE // the channel may be become reachable after having been unreachable. // if the callback was collected during this phase it results in a @@ -74,7 +78,6 @@ STATIC mp_obj_t signal_core_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); static inline void channel_core_verify(channel_core_obj_t *channel_core) { - channel_core->chan = *channel_core->chan_ref; if (!channel_core->chan) mp_raise_msg(&mp_type_ReferenceError, MP_ERROR_TEXT("channel was deleted")); @@ -252,10 +255,27 @@ static mp_obj_t create_channel_plugin(bl00mbox_channel_t *chan) { return MP_OBJ_FROM_PTR(self); } +static mp_obj_t create_weak_channel_core(channel_core_obj_t *self) { + channel_core_obj_t *weak = m_new_obj_with_finaliser(channel_core_obj_t); + weak->base.type = &channel_core_type; + weak->weak = MP_OBJ_FROM_PTR(weak); + weak->chan = self->chan; + weak->chan->weak_parent_self_ref = &weak->chan; + + // only the real channel should prevent these from being gc'd + weak->sources_mx = MP_OBJ_NULL; + weak->channel_plugin = MP_OBJ_NULL; +#ifdef BL00MBOX_CALLBACKS_FUNSAFE + weak->callback = MP_OBJ_NULL; +#endif + return MP_OBJ_FROM_PTR(weak); +} + STATIC mp_obj_t channel_core_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); channel_core_obj_t *self = m_new_obj_with_finaliser(channel_core_obj_t); + self->chan = NULL; // must initialize if obj gets gc'd due to exception self->base.type = &channel_core_type; self->sources_mx = mp_obj_new_list(0, NULL); @@ -269,8 +289,7 @@ STATIC mp_obj_t channel_core_make_new(const mp_obj_type_t *type, size_t n_args, chan->parent = self; self->chan = chan; - self->chan_ref = &self->chan; - self->fake = false; + self->weak = create_weak_channel_core(self); self->channel_plugin = create_channel_plugin(chan); #ifdef BL00MBOX_CALLBACKS_FUNSAFE self->callback = mp_const_none; @@ -279,29 +298,17 @@ STATIC mp_obj_t channel_core_make_new(const mp_obj_type_t *type, size_t n_args, return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t mp_channel_core_make_fake(mp_obj_t self_in) { - channel_core_obj_t *fake = m_new_obj_with_finaliser(channel_core_obj_t); - fake->base.type = &channel_core_type; - +STATIC mp_obj_t mp_channel_core_get_weak(mp_obj_t self_in) { channel_core_obj_t *self = MP_OBJ_TO_PTR(self_in); channel_core_verify(self); - - fake->sources_mx = mp_const_none; - fake->chan = self->chan; - fake->chan_ref = self->chan_ref; - fake->fake = true; - fake->channel_plugin = self->channel_plugin; -#ifdef BL00MBOX_CALLBACKS_FUNSAFE - fake->callback = MP_OBJ_NULL; -#endif - return MP_OBJ_FROM_PTR(fake); + return self->weak; } -MP_DEFINE_CONST_FUN_OBJ_1(mp_channel_core_make_fake_obj, - mp_channel_core_make_fake); +MP_DEFINE_CONST_FUN_OBJ_1(mp_channel_core_get_weak_obj, + mp_channel_core_get_weak); mp_obj_t mp_channel_core_del(mp_obj_t self_in) { channel_core_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->chan && !self->fake) { + if (self->chan && (self->weak != self_in)) { bl00mbox_log_info("destroyed channel %s", self->chan->name); bl00mbox_channel_destroy(self->chan); } @@ -392,7 +399,7 @@ STATIC void channel_core_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = mp_obj_new_str(ret, strlen(ret)); free(ret); } else if (attr == MP_QSTR_channel_plugin) { - dest[0] = self->channel_plugin; + dest[0] = ((channel_core_obj_t *)chan->parent)->channel_plugin; } else if (attr == MP_QSTR_callback) { #ifdef BL00MBOX_CALLBACKS_FUNSAFE dest[0] = ((channel_core_obj_t *)chan->parent)->callback; @@ -419,8 +426,8 @@ STATIC const mp_rom_map_elem_t channel_core_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_channel_core_del_obj) }, { MP_ROM_QSTR(MP_QSTR_delete), MP_ROM_PTR(&mp_channel_core_delete_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&mp_channel_core_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_make_fake), - MP_ROM_PTR(&mp_channel_core_make_fake_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_weak), + MP_ROM_PTR(&mp_channel_core_get_weak_obj) }, { MP_ROM_QSTR(MP_QSTR_get_connected_mx), MP_ROM_PTR(&mp_channel_core_get_connected_mx_obj) }, { MP_ROM_QSTR(MP_QSTR_get_plugins), -- GitLab