Skip to content

Commit 1f49a25

Browse files
committed
fix: ensure context creation and execution occur on platform threads to prevent thread-local issues
1 parent 77599d4 commit 1f49a25

1 file changed

Lines changed: 42 additions & 39 deletions

File tree

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

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

85-
// Pre-populate pool
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).
8691
for (var c = 0;c < POOL_SIZE;c++) {
87-
pool.offer(newContext());
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+
}
8898
}
8999
}
90100

@@ -134,20 +144,23 @@ private void release(Context ctx) {
134144
* @throws Exception if the task throws an exception
135145
*/
136146
public <T> T executeWithContext(ContextTask<T> task) throws Exception {
137-
Context ctx = acquire();
138-
try {
139-
// Context.enter()/leave() must run on a platform thread, see PolyglotThreadUtils
140-
return PolyglotThreadUtils.onPlatformThread(() -> {
147+
// acquire/enter/task/leave/release must all happen on the very same
148+
// platform thread: if the pool is empty, acquire() calls newContext()
149+
// which creates a Context that must be entered on the same thread
150+
// (see PolyglotThreadUtils / oracle/graal#7520).
151+
return PolyglotThreadUtils.onPlatformThread(() -> {
152+
Context ctx = acquire();
153+
try {
141154
ctx.enter();
142155
try {
143156
return task.run(ctx);
144157
} finally {
145158
ctx.leave();
146159
}
147-
});
148-
} finally {
149-
release(ctx);
150-
}
160+
} finally {
161+
release(ctx);
162+
}
163+
});
151164
}
152165

153166
/**
@@ -157,21 +170,20 @@ public <T> T executeWithContext(ContextTask<T> task) throws Exception {
157170
* @throws Exception if the task throws an exception
158171
*/
159172
public void executeWithContext(VoidContextTask task) throws Exception {
160-
Context ctx = acquire();
161-
try {
162-
// Context.enter()/leave() must run on a platform thread, see PolyglotThreadUtils
163-
PolyglotThreadUtils.onPlatformThread(() -> {
173+
PolyglotThreadUtils.onPlatformThread(() -> {
174+
Context ctx = acquire();
175+
try {
164176
ctx.enter();
165177
try {
166178
task.run(ctx);
167179
} finally {
168180
ctx.leave();
169181
}
170-
return null;
171-
});
172-
} finally {
173-
release(ctx);
174-
}
182+
} finally {
183+
release(ctx);
184+
}
185+
return null;
186+
});
175187
}
176188

177189
/**
@@ -238,28 +250,19 @@ public static Context newContext(Engine engine, String name, Configuration conf,
238250
LOGGER.trace("modules-replacements ignored (removed in GraalVM 25.1): {}", modulesReplacements);
239251
}
240252

241-
try {
242-
// Context creation touches thread locals, must run on a platform thread, see PolyglotThreadUtils
243-
return PolyglotThreadUtils.onPlatformThread(() -> {
244-
var ctx = Context.newBuilder().engine(engine)
245-
.allowAllAccess(true)
246-
.allowHostAccess(HostAccess.ALL)
247-
.allowHostClassLookup(className -> true)
248-
.allowIO(IOAccess.ALL)
249-
.allowExperimentalOptions(true)
250-
.allowValueSharing(true) // Enable value sharing to reduce marshalling overhead
251-
.options(OPTS)
252-
.build();
253+
var ctx = Context.newBuilder().engine(engine)
254+
.allowAllAccess(true)
255+
.allowHostAccess(HostAccess.ALL)
256+
.allowHostClassLookup(className -> true)
257+
.allowIO(IOAccess.ALL)
258+
.allowExperimentalOptions(true)
259+
.allowValueSharing(true) // Enable value sharing to reduce marshalling overhead
260+
.options(OPTS)
261+
.build();
253262

254-
addBindings(ctx, name, conf, logger, mclient);
263+
addBindings(ctx, name, conf, logger, mclient);
255264

256-
return ctx;
257-
});
258-
} catch (RuntimeException re) {
259-
throw re;
260-
} catch (Exception e) {
261-
throw new IllegalStateException("Error creating polyglot context", e);
262-
}
265+
return ctx;
263266
}
264267

265268
private static void addBindings(Context ctx,

0 commit comments

Comments
 (0)