mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 02:23:34 +00:00
Fix autosave timer self-unregister crash risk
The periodic autosave timer called reset_timer() at the end of its own callback, which unregistered the timer that was still executing (itself). Blender frees the timer's internal registry entry on that manual unregister, then frees it again when the callback returns None - a double free that corrupts the heap and can crash Blender later, once the corrupted memory is reused. Reschedule by returning the next interval from the callback instead, which is the safe, documented way to repeat a bpy.app.timers callback. External reset_timer() calls (from SaveProject, LoadProject, AutosavePrompt) are unaffected since they run from a separate call stack (UI events), not from inside the timer. Found while investigating a segfault reported when cancelling the autosave recovery popup; not itself the cause of that crash (see the following commit), but the same reentrant-unregister pattern and a real, independent latent bug in the periodic reminder path. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -96,9 +96,16 @@ class Autosave:
|
||||
if not cls.is_eligible():
|
||||
return
|
||||
|
||||
def on_timer() -> None:
|
||||
def on_timer() -> Union[float, None]:
|
||||
cls._on_timer_expired()
|
||||
return None
|
||||
# Reschedule by returning the next interval rather than calling
|
||||
# reset_timer(), which would unregister this timer from within
|
||||
# its own callback. Blender frees the timer's internal registry
|
||||
# entry on that manual unregister, then frees it again when the
|
||||
# callback returns - a double free that corrupts the heap and
|
||||
# crashes Blender shortly after (e.g. when the prompt dialog
|
||||
# spawned below is next interacted with).
|
||||
return cls.get_interval_seconds() if cls.is_eligible() else None
|
||||
|
||||
global _timer_callback
|
||||
_timer_callback = on_timer
|
||||
@@ -120,7 +127,6 @@ class Autosave:
|
||||
cls.perform_backup(bpy.context)
|
||||
except Exception as error:
|
||||
print(f"Bonsai: autosave backup failed: {error}")
|
||||
cls.reset_timer()
|
||||
|
||||
@classmethod
|
||||
def perform_backup(cls, context: bpy.types.Context) -> None:
|
||||
|
||||
Reference in New Issue
Block a user