Skip to content

Commit c1f1b89

Browse files
committed
fix: ensure Source.findLanguage and context operations run on platform thread in PolyglotDeployer and JSInterceptorFactory to prevent thread-local issues
See #663
1 parent 322e082 commit c1f1b89

2 files changed

Lines changed: 19 additions & 14 deletions

File tree

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@
7878
* @author Andrea Di Cesare {@literal <andrea@softinstigate.com>}
7979
*/
8080
@RegisterPlugin(
81-
name = "polyglotDeployer",
82-
description = "handles GraalVM polyglot plugins",
83-
enabledByDefault = true)
81+
name = "polyglotDeployer",
82+
description = "handles GraalVM polyglot plugins",
83+
enabledByDefault = true)
8484
public class PolyglotDeployer implements Initializer {
8585

8686
private static final Logger LOGGER = LoggerFactory.getLogger(PolyglotDeployer.class);
@@ -353,14 +353,18 @@ private List<Path> findDeclaredPlugins(final Path path, final String prop, final
353353
if (checkPluginFiles) {
354354
if (Files.isRegularFile(pluginPath)) {
355355
try {
356-
final var language = PolyglotClassloaderHelper.withPluginsClassloaderResult(
357-
() -> Source.findLanguage(pluginPath.toFile()));
356+
// Source.findLanguage() touches Truffle internals;
357+
// this lambda may run on a virtual thread (file watcher),
358+
// so dispatch to a platform thread (see PolyglotThreadUtils)
359+
final var language = PolyglotThreadUtils.onPlatformThread(
360+
() -> PolyglotClassloaderHelper.withPluginsClassloaderResult(
361+
() -> Source.findLanguage(pluginPath.toFile())));
358362
if ("js".equals(language)) {
359363
ret.add(pluginPath);
360364
} else {
361365
LOGGER.warn("{} is not javascript", pluginPath.toAbsolutePath());
362366
}
363-
} catch (final IOException e) {
367+
} catch (final Exception e) {
364368
LOGGER.warn("{} is not javascript", pluginPath.toAbsolutePath(), e);
365369
}
366370
} else {
@@ -454,8 +458,8 @@ private void deployNodeService(final Path pluginPath) throws IOException {
454458
DEPLOYEES.put(pluginPath.toAbsolutePath(), srv);
455459

456460
LOGGER.info(ansi().fg(GREEN).a(
457-
"Service '{}' deployed at URI '{}' with description: '{}'. Secured: {}. Uri match policy: {}")
458-
.reset().toString(), srv.name(), srv.uri(), srv.getDescription(), srv.secured(),
461+
"Service '{}' deployed at URI '{}' with description: '{}'. Secured: {}. Uri match policy: {}")
462+
.reset().toString(), srv.name(), srv.uri(), srv.getDescription(), srv.secured(),
459463
srv.matchPolicy());
460464
} catch (IOException | InterruptedException | ExecutionException | TimeoutException ex) {
461465
LOGGER.error("Error deploying node service {}", pluginPath, ex);

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ public JSInterceptorFactory(Optional<MongoClient> mclient, Configuration config)
9494
LOGGER.debug("Enabling require for interceptor {} with require-cwd {} ", pluginPath, requireCwdPath);
9595
}
9696

97+
// Source.findLanguage() and all Context/Value operations must run on a
98+
// platform thread (see PolyglotThreadUtils). The whole block is dispatched
99+
// as a single task so eval() and subsequent Value member access happen on
100+
// the very same thread that entered the context.
101+
try {
102+
return PolyglotThreadUtils.onPlatformThread(() -> {
103+
97104
// check that the plugin script is js (use PluginsClassloader so js-language is visible)
98105
final var language = PolyglotClassloaderHelper.withPluginsClassloaderResult(
99106
() -> Source.findLanguage(pluginPath.toFile()));
@@ -107,12 +114,6 @@ public JSInterceptorFactory(Optional<MongoClient> mclient, Configuration config)
107114
var sindexPath = pluginPath.toUri().toString();
108115
LOGGER.debug("Resolved interceptor path: {}", sindexPath);
109116

110-
// Context creation, eval and all Value member access must happen on the very
111-
// same platform thread: a Value returned by eval() is only safely readable on
112-
// the thread the context is entered on (see PolyglotThreadUtils), so the whole
113-
// block below -- not just the eval() calls -- is dispatched as a single task.
114-
try {
115-
return PolyglotThreadUtils.onPlatformThread(() -> {
116117
var ctx = ContextQueue.newContext(engine, "foo", config, LOGGER, mclient, "", contextOptions);
117118
ctx.enter();
118119
try {

0 commit comments

Comments
 (0)