Skip to content
This repository was archived by the owner on Oct 30, 2026. It is now read-only.

Sync bindings 2137b6c: animation and timer callbacks are released - #33

Merged
bdbarnett merged 2 commits into
mainfrom
fix-struct-cb
Oct 12, 2026
Merged

bdbarnett merged 2 commits into
mainfrom
fix-struct-cb

Conversation

@bdbarnett

Copy link
Copy Markdown
Contributor

Third step for #31: the sync of lvgl-bindings#34 (struct setters pass the holder, lvpy_timer_bind / lvpy_anim_bind after lv_timer_create, lv_timer_set_cb and lv_anim_start, LV_USE_EXT_DATA on for CPython), with the test and the docs line. tests/test_struct_callback_lifetime.py: all nine checks fail on 9.5.50; pass here; a variant where the running animation copy takes no reference segfaults under MALLOC_PERTURB_. 50 animation plus 50 timer cycles with 20 KB per callback: under 64 KB growth, 3 MB before.

…d animations release it

Callbacks set on a struct (lv_anim_t, lv_timer_t, ...) live in a dict in
the struct's user_data, like event callbacks. For events the dict is
released when the registration ends (#30); for structs nothing released
it, so every animation or timer callback leaked (#31).

Registry entries now carry the holder that owns the reference. An event
registration's holder is NULL and takes one reference per call, as
before. A struct's holder is the struct itself: the generated setters
pass it as containing_struct (lvgl-bindings), every callback set on the
struct shares one reference, and the reference goes when the struct's
owner lets go of it: py_lv_struct_dealloc for a struct the Python
wrapper owns; LVGL's ext_data free_cb for a timer (lvpy_timer_bind,
called after lv_timer_create and lv_timer_set_cb) and for a running
animation (lvpy_anim_bind, called after lv_anim_start, where the copy
LVGL made takes its own reference so the Python struct can start it
again). Those two hooks compile to nothing until lv_conf.h enables
LV_USE_EXT_DATA for the CPython build, which the bindings change does.

Inert on its own: the generated code calls none of it yet. The sync that
follows the bindings change brings the test.

Part of #31.
…n and timer callbacks are released

The generated struct setters now pass the struct as containing_struct,
lv_timer_create / lv_timer_set_cb call lvpy_timer_bind and lv_anim_start
calls lvpy_anim_bind, and lv_conf.h enables LV_USE_EXT_DATA for the
CPython build. With the runtime from #32 that means a callback set on an
lv.anim_t lives while the Python struct or a running copy of it exists,
and a timer's callback lives while the timer does; LVGL's ext_data
free_cb releases them on every path that frees the timer or animation.

tests/test_struct_callback_lifetime.py pins it: released after
completion once the struct is dropped; survives a dropped struct while
running; restart from the same struct; released on anim_delete_all after
deleted_cb ran; a timer released on delete, when its repeat count runs
out, and when it deletes itself from its own callback; set_cb replaces
without leaking; 50 animation plus 50 timer cycles with 20 KB per
callback grow the heap by under 64 KB (3 MB before). All nine checks fail
on 9.5.50. A variant where the running copy takes no reference segfaults
under MALLOC_PERTURB_ while the dropped struct's animation is still
running, which is why the copy holds its own reference.

For #31.
@bdbarnett
bdbarnett merged commit 9beb7e4 into main Oct 12, 2026
1 check passed
@bdbarnett
bdbarnett deleted the fix-struct-cb branch October 12, 2026 07:25
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant