Skip to content

fix: serialize V8 isolate creation across threads#222

Open
benpalevsky wants to merge 1 commit into
godotjs:mainfrom
hatgg:3-serialize-isolate-create
Open

fix: serialize V8 isolate creation across threads#222
benpalevsky wants to merge 1 commit into
godotjs:mainfrom
hatgg:3-serialize-isolate-create

Conversation

@benpalevsky

Copy link
Copy Markdown
Contributor

Problem

GodotJS can start extra V8 "environments" on background threads. For example, the editor's resource loader creates short-lived, pooled "shadow" environments on worker threads while the main environment is also starting up.

The first v8::Isolate::New call in a process triggers one-time initialization inside V8 that is not thread-safe. (GodotJS's own GlobalInitialize::init() is already a thread-safe magic static; the race is V8-internal, kicked off by the first concurrent isolate creations.) So when several environments create isolates at the same moment on different threads, they race through that lazy init and the engine crashes (segfault) deep inside V8 (in LazyInstanceImpl<Mutex>::InitInstance, on optimized builds).

What this changes

Put a single process-wide lock around the first-time V8 setup and the isolate creation in the Environment constructor (bridge/jsb_environment.cpp), so only one thread runs that block at a time. Everything after it (setting callbacks, creating the context, and so on) stays outside the lock and runs normally.

The lock is one function-local static mutex, which means every thread shares the same single lock — that's what makes the protection process-wide instead of per-environment. Most of the diff is just re-indentation from wrapping the block.

How to verify

Rebuild the engine and stress concurrent environment creation — for example a batch reimport that spawns shadow environments on worker threads — and confirm it no longer crashes on startup.

A changeset is included.

Shadow/worker Environments are constructed on worker threads via create_shadow_environment, concurrently with the main Environment and each other. Unsynchronized first-time v8::Isolate::New races V8's process-global lazy global initialization and SIGSEGVs deep in v8::base::LazyInstanceImpl<Mutex>::InitInstance on optimized builds. Wrap GlobalInitialize::init() and v8::Isolate::New in a process-global Mutex so first-time V8 init runs exactly once across all threads.
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.

1 participant