Skip to content

Commit 3bc1b4f

Browse files
committed
fix: create Contexts and Engine directly on the calling thread to prevent thread-local issues
1 parent 1f49a25 commit 3bc1b4f

3 files changed

Lines changed: 21 additions & 21 deletions

File tree

polyglot/src/main/java/org/restheart/polyglot/ContextQueue.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,14 @@ public ContextQueue(Engine engine, String name, Configuration conf, Logger logge
8282
this.modulesReplacements = modulesReplacements;
8383
this.OPTS = OPTS;
8484

85-
// Pre-populate pool: each Context must be created on a platform thread.
86-
// Do NOT delegate to newContext() here -- its callers (args(), create())
87-
// are already inside onPlatformThread, so newContext() must not wrap again
88-
// (nested dispatch creates the Context on a *different* platform thread
89-
// than the one that will enter it, which corrupts Truffle's
90-
// DefaultContextThreadLocal bookkeeping, see oracle/graal#7520).
85+
// Pre-populate pool: create Contexts directly on the calling thread.
86+
// The calling thread is the main thread during startup (platform thread
87+
// where Engine was just created), so DefaultContextThreadLocal is already
88+
// initialised. We must NOT dispatch to a pool thread here: Truffle's
89+
// bookkeeping breaks when a Context is created on one thread and entered
90+
// on another (see oracle/graal#7520).
9191
for (var c = 0;c < POOL_SIZE;c++) {
92-
try {
93-
pool.offer(PolyglotThreadUtils.onPlatformThread(
94-
() -> newContext(engine, name, conf, logger, mclient, modulesReplacements, OPTS)));
95-
} catch (Exception e) {
96-
LOGGER.warn("Error pre-creating polyglot context", e);
97-
}
92+
pool.offer(newContext(engine, name, conf, logger, mclient, modulesReplacements, OPTS));
9893
}
9994
}
10095

polyglot/src/main/java/org/restheart/polyglot/JSPlugin.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.graalvm.polyglot.Engine;
2727
import org.graalvm.polyglot.Source;
2828
import org.restheart.configuration.Configuration;
29+
import org.restheart.polyglot.PolyglotClassloaderHelper;
2930
import org.restheart.polyglot.services.JSServiceArgs;
3031
import org.slf4j.Logger;
3132
import org.slf4j.LoggerFactory;
@@ -39,12 +40,16 @@ public abstract class JSPlugin {
3940

4041
static {
4142
try {
42-
// Must be a plain method reference to a method NOT declared here: a lambda
43-
// body written inside this static initializer would become a private
44-
// synthetic method of JSPlugin, and invoking it from the platform thread
45-
// would then require JSPlugin's own <clinit> to finish -- which is exactly
46-
// what's blocked waiting for this platform thread, causing a deadlock.
47-
engine = PolyglotThreadUtils.onPlatformThread(PolyglotThreadUtils::createEngine);
43+
// Engine.create() is called directly on the main thread (not dispatched
44+
// to a pool thread). This avoids Truffle's DefaultContextThreadLocal
45+
// being initialised on a throwaway pool thread, which corrupts the
46+
// bookkeeping when a *different* thread later enters a Context
47+
// (see oracle/graal#7520).
48+
//
49+
// No deadlock risk: PolyglotClassloaderHelper and Engine are both on
50+
// different classes, so referencing them from this <clinit> never
51+
// re-enters JSPlugin's own class initialisation.
52+
engine = PolyglotClassloaderHelper.withPluginsClassloaderResult(Engine::create);
4853
} catch (Exception e) {
4954
throw new IllegalStateException("Error creating polyglot Engine", e);
5055
}

polyglot/src/main/java/org/restheart/polyglot/interceptors/JSInterceptorFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ public JSInterceptorFactory(Optional<MongoClient> mclient, Configuration config)
6565
this.mclient = mclient;
6666
this.config = config;
6767
try {
68-
// Engine.create() touches Truffle thread locals, must run on a platform thread (see PolyglotThreadUtils)
69-
// and needs PluginsClassloader so ServiceLoader can find js-language's TruffleLanguageProvider
70-
this.engine = PolyglotThreadUtils.onPlatformThread(PolyglotThreadUtils::createEngine);
68+
// Engine.create() called directly on the calling thread (main thread during
69+
// startup). See JSPlugin.<clinit> for the rationale.
70+
this.engine = PolyglotClassloaderHelper.withPluginsClassloaderResult(Engine::create);
7171
} catch (Exception e) {
7272
throw new IllegalStateException("Error creating polyglot Engine", e);
7373
}

0 commit comments

Comments
 (0)