Skip to content

Commit 97512a8

Browse files
committed
fix: ensure context closure runs on platform thread in ContextQueue, JSInterceptorFactory, and JSStringService to prevent thread-local issues
See: #663
1 parent 47cfa96 commit 97512a8

3 files changed

Lines changed: 35 additions & 8 deletions

File tree

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,16 @@ private Context acquire() {
110110
*/
111111
private void release(Context ctx) {
112112
if (!pool.offer(ctx)) {
113-
// Pool is full, close the context
114-
ctx.close();
115-
LOGGER.debug("Pool full, closed excess context");
113+
try {
114+
// Context.close() touches thread locals, must run on a platform thread, see PolyglotThreadUtils
115+
PolyglotThreadUtils.onPlatformThread(() -> {
116+
ctx.close();
117+
return null;
118+
});
119+
LOGGER.debug("Pool full, closed excess context");
120+
} catch (Exception e) {
121+
LOGGER.warn("Error closing excess context", e);
122+
}
116123
}
117124
}
118125

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import java.util.Map;
2828
import java.util.Optional;
2929

30-
import org.graalvm.polyglot.Context;
3130
import org.restheart.polyglot.PolyglotClassloaderHelper;
3231
import org.restheart.polyglot.PolyglotThreadUtils;
3332
import org.graalvm.polyglot.Engine;
@@ -68,7 +67,7 @@ public JSInterceptorFactory(Optional<MongoClient> mclient, Configuration config)
6867
try {
6968
// Engine.create() touches Truffle thread locals, must run on a platform thread (see PolyglotThreadUtils)
7069
// and needs PluginsClassloader so ServiceLoader can find js-language's TruffleLanguageProvider
71-
this.engine = PolyglotThreadUtils.onPlatformThread(() -> PolyglotClassloaderHelper.withPluginsClassloaderResult(Engine::create));
70+
this.engine = PolyglotThreadUtils.onPlatformThread(PolyglotThreadUtils::createEngine);
7271
} catch (Exception e) {
7372
throw new IllegalStateException("Error creating polyglot Engine", e);
7473
}
@@ -108,7 +107,8 @@ public JSInterceptorFactory(Optional<MongoClient> mclient, Configuration config)
108107
var sindexPath = pluginPath.toUri().toString();
109108
LOGGER.debug("Resolved interceptor path: {}", sindexPath);
110109

111-
try (Context ctx = ContextQueue.newContext(engine, "foo", config, LOGGER, mclient, "", contextOptions)) {
110+
var ctx = ContextQueue.newContext(engine, "foo", config, LOGGER, mclient, "", contextOptions);
111+
try {
112112

113113
// ******** evaluate and check options
114114
var optionsScript = "import { options } from '" + sindexPath + "'; options;";
@@ -352,6 +352,16 @@ public JSInterceptorFactory(Optional<MongoClient> mclient, Configuration config)
352352
interceptor.getClass().getName(),
353353
interceptor,
354354
new HashMap<>());
355+
} finally {
356+
try {
357+
// Context.close() touches thread locals, must run on a platform thread, see PolyglotThreadUtils
358+
PolyglotThreadUtils.onPlatformThread(() -> {
359+
ctx.close();
360+
return null;
361+
});
362+
} catch (Exception e) {
363+
LOGGER.warn("Error closing context for {}", pluginPath, e);
364+
}
355365
}
356366
}
357367

polyglot/src/main/java/org/restheart/polyglot/services/JSStringService.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.util.HashMap;
2727
import java.util.Optional;
2828

29-
import org.graalvm.polyglot.Context;
3029
import org.graalvm.polyglot.Source;
3130
import org.restheart.polyglot.PolyglotClassloaderHelper;
3231
import org.restheart.polyglot.PolyglotThreadUtils;
@@ -97,7 +96,8 @@ private static JSServiceArgs args(Path pluginPath, Optional<MongoClient> mclient
9796
LOGGER.trace("Enabling require for service {} with require-cwd {} ", pluginPath, requireCwdPath);
9897
}
9998

100-
try (Context ctx = ContextQueue.newContext(engine(), "foo", config, LOGGER, mclient, "", contextOptions)) {
99+
var ctx = ContextQueue.newContext(engine(), "foo", config, LOGGER, mclient, "", contextOptions);
100+
try {
101101
// check that the plugin script is js (use PluginsClassloader so js-language is visible)
102102
final var language = PolyglotClassloaderHelper.withPluginsClassloaderResult(
103103
() -> Source.findLanguage(pluginPath.toFile()));
@@ -162,6 +162,16 @@ private static JSServiceArgs args(Path pluginPath, Optional<MongoClient> mclient
162162
checkHandle(handle, pluginPath);
163163

164164
return new JSServiceArgs(name, description, uri, secured, modulesReplacements, matchPolicy, handleSource, config, mclient, contextOptions);
165+
} finally {
166+
try {
167+
// Context.close() touches thread locals, must run on a platform thread, see PolyglotThreadUtils
168+
PolyglotThreadUtils.onPlatformThread(() -> {
169+
ctx.close();
170+
return null;
171+
});
172+
} catch (Exception e) {
173+
LOGGER.warn("Error closing context for {}", pluginPath, e);
174+
}
165175
}
166176
}
167177

0 commit comments

Comments
 (0)