Crash on exit: double free or corruption #7

Closed
opened 2026-09-27 03:40:57 +00:00 by sky_line · 2 comments
Owner

Viewer sometimes aborts at shutdown with 'double free or corruption (out)' and hangs while dumping core. Suspected boost::wave/spirit static destructors (LSL preprocessor). Binaries are now archived per launch (~/.cache/alchemy-bins) so the next occurrence can be symbolized.

Viewer sometimes aborts at shutdown with 'double free or corruption (out)' and hangs while dumping core. Suspected boost::wave/spirit static destructors (LSL preprocessor). Binaries are now archived per launch (~/.cache/alchemy-bins) so the next occurrence can be symbolized.
Author
Owner

Symbolized occurrence (2026-09-27, build with GL-B8, SIGSEGV exit 139 at logout). It confirms the suspicion:

libc exit() -> static destructors
  boost::shared_ptr<spirit::classic::impl::object_with_id_base_supply<unsigned long>>::~shared_ptr
  sp_counted_base::release -> checked_delete
  object_with_id_base_supply<unsigned long>::~object_with_id_base_supply   (object_with_id.ipp:33)
  boost::mutex::~mutex -> pthread_mutex_destroy   <- SIGSEGV

object_with_id_base_supply is Boost.Spirit Classic's grammar/rule ID pool, a function-local static shared_ptr shared by every grammar instance. The only Spirit/Wave user in the viewer is the LSL preprocessor (fslslpreproc / boost::wave). At exit the pool is released during static destruction after (or while) the objects that reference it are torn down, so its mutex is already gone. That's a static-destruction-order problem, and the same mutex family as the old Ctrl+S deadlock (#8).

Fix options:

  1. Skip static destructors after an orderly shutdown: once LLAppViewer::cleanup() has finished (settings saved, logs flushed, threads joined), end the process with _exit(0) / std::quick_exit. It removes this whole class of exit crashes (Boost.Spirit, other third-party statics), and the OS reclaims memory anyway. Needs a check that nothing important runs in static dtors (log file close: flush explicitly first).
  2. Scope fix: make the preprocessor's Wave context and grammar objects die before main returns (explicit teardown in LLAppViewer::cleanup), so the ID pool isn't released during static destruction. More surgical, but other library statics can still bite.

Recommend (1), with (2) if a problem shows up.

**Symbolized occurrence (2026-09-27, build with GL-B8, SIGSEGV exit 139 at logout).** It confirms the suspicion: ``` libc exit() -> static destructors boost::shared_ptr<spirit::classic::impl::object_with_id_base_supply<unsigned long>>::~shared_ptr sp_counted_base::release -> checked_delete object_with_id_base_supply<unsigned long>::~object_with_id_base_supply (object_with_id.ipp:33) boost::mutex::~mutex -> pthread_mutex_destroy <- SIGSEGV ``` `object_with_id_base_supply` is Boost.Spirit Classic's grammar/rule ID pool, a function-local static `shared_ptr` shared by every grammar instance. The only Spirit/Wave user in the viewer is the LSL preprocessor (fslslpreproc / boost::wave). At exit the pool is released during static destruction after (or while) the objects that reference it are torn down, so its mutex is already gone. That's a static-destruction-order problem, and the same mutex family as the old Ctrl+S deadlock (#8). **Fix options:** 1. **Skip static destructors after an orderly shutdown:** once `LLAppViewer::cleanup()` has finished (settings saved, logs flushed, threads joined), end the process with `_exit(0)` / `std::quick_exit`. It removes this whole class of exit crashes (Boost.Spirit, other third-party statics), and the OS reclaims memory anyway. Needs a check that nothing important runs in static dtors (log file close: flush explicitly first). 2. **Scope fix:** make the preprocessor's Wave context and grammar objects die before `main` returns (explicit teardown in LLAppViewer::cleanup), so the ID pool isn't released during static destruction. More surgical, but other library statics can still bite. Recommend (1), with (2) if a problem shows up.
Author
Owner

Verified done: the symbolized backtrace (Boost.Spirit object_with_id_base_supply mutex destroyed during static destruction) was an ODR violation, fixed by defining BOOST_SPIRIT_THREADSAFE/PHOENIX_THREADSAFE project-wide; the remaining exit-time static-destruction crash (sVKAlphaImages / media textures, #275) is fixed too, and a live run exits cleanly with no segfault or core dump. Commits: cc4596f6f8 (Spirit ODR), 4932f2a8c9 (#275 texture teardown order). Shipped in v0.11.0-beta.1 (Spirit fix) / v1.0.0-beta.4 (#275 fix).

Verified done: the symbolized backtrace (Boost.Spirit object_with_id_base_supply mutex destroyed during static destruction) was an ODR violation, fixed by defining BOOST_SPIRIT_THREADSAFE/PHOENIX_THREADSAFE project-wide; the remaining exit-time static-destruction crash (sVKAlphaImages / media textures, #275) is fixed too, and a live run exits cleanly with no segfault or core dump. Commits: cc4596f6f8 (Spirit ODR), 4932f2a8c9 (#275 texture teardown order). Shipped in v0.11.0-beta.1 (Spirit fix) / v1.0.0-beta.4 (#275 fix).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sky_line/Slipstream#7
No description provided.