Skip to content

Merge WorkQueue into AppRuntime#149

Open
bghgary wants to merge 4 commits intoBabylonJS:mainfrom
bghgary:refactor/merge-workqueue-into-appruntime
Open

Merge WorkQueue into AppRuntime#149
bghgary wants to merge 4 commits intoBabylonJS:mainfrom
bghgary:refactor/merge-workqueue-into-appruntime

Conversation

@bghgary
Copy link
Copy Markdown
Contributor

@bghgary bghgary commented Apr 8, 2026

Merge WorkQueue into AppRuntime to eliminate split-lifetime issues.

Motivation

WorkQueue was a separate class with its own thread, dispatcher, and cancel source. AppRuntime captured this in lambdas passed to WorkQueue, creating split-lifetime issues where the worker thread could access partially-destroyed AppRuntime members during shutdown.

Changes

  • Thread, dispatcher, cancel source, env, and suspension lock are now direct AppRuntime members
  • Destruction order is deterministic (no cross-object lifetime issues)
  • WorkQueue.h and WorkQueue.cpp removed
  • AppRuntime_JSI.cpp: TaskRunnerAdapter now uses AppRuntime::Dispatch instead of WorkQueue::Append, and uses std::move(task) instead of task.release() for shared_ptr construction

Behavioral note

JSI tasks now route through Dispatch, which wraps them in the Execute/exception-handler. Previously, uncaught exceptions from JSI tasks would crash the process. They are now handled by UnhandledExceptionHandler like all other dispatched work.

The deadlock fix from #147 is preserved.

WorkQueue was a separate class with its own thread, dispatcher, and cancel
source. AppRuntime captured 'this' in lambdas passed to WorkQueue, creating
split-lifetime issues where the worker thread could access partially-destroyed
AppRuntime members during shutdown.

This change inlines WorkQueue's functionality directly into AppRuntime:
- Thread, dispatcher, cancel source, and env are now AppRuntime members
- Destruction order is deterministic (no cross-object lifetime issues)
- WorkQueue.h and WorkQueue.cpp are removed
- AppRuntime_JSI.cpp updated to use AppRuntime::Dispatch instead of
  WorkQueue::Append, and fixed task.release() to std::move(task) for
  shared_ptr construction

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 8, 2026 16:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the AppRuntime threading model by inlining the former WorkQueue implementation into AppRuntime, aiming to eliminate split-lifetime shutdown hazards caused by cross-object thread/dispatcher ownership.

Changes:

  • Removed the WorkQueue class/files and moved its thread, dispatcher, cancellation source, env tracking, and suspension lock into AppRuntime.
  • Updated AppRuntime to manage the worker thread lifecycle directly (including the deadlock-avoidance “cancel + enqueue no-op” wake-up).
  • Updated the JSI task runner adapter to post tasks via AppRuntime::Dispatch and modernized shared ownership creation for tasks.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Core/AppRuntime/Source/WorkQueue.h Removed (WorkQueue API eliminated).
Core/AppRuntime/Source/WorkQueue.cpp Removed (WorkQueue implementation merged into AppRuntime).
Core/AppRuntime/Source/AppRuntime.cpp Moved WorkQueue run-loop/suspend/resume/shutdown logic into AppRuntime and starts the worker thread directly.
Core/AppRuntime/Source/AppRuntime_JSI.cpp TaskRunnerAdapter now targets AppRuntime instead of WorkQueue; task ownership construction updated.
Core/AppRuntime/Include/Babylon/AppRuntime.h Added dispatcher/cancellation/thread members and an internal Append helper.
Core/AppRuntime/CMakeLists.txt Removed WorkQueue sources from the AppRuntime target.
Comments suppressed due to low confidence (1)

Core/AppRuntime/Source/AppRuntime.cpp:17

  • The worker thread is started in the member initializer list. If any code in the constructor body throws after m_thread is created (e.g., Dispatch(...) or allocations inside it), stack unwinding will destroy m_thread while it is still joinable, which will call std::terminate because AppRuntime’s destructor is not run for a partially-constructed object. Consider starting the thread after all potentially-throwing constructor work (or using a scope guard/RAII thread wrapper that joins on unwind).
    AppRuntime::AppRuntime(Options options)
        : m_options{std::move(options)}
        , m_thread{[this] { RunPlatformTier(); }}
    {
        Dispatch([this](Napi::Env env) {
            JsRuntime::CreateForJavaScript(env, [this](auto func) { Dispatch(std::move(func)); });
        });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

bghgary and others added 3 commits April 8, 2026 09:44
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants