Skip to content

Commit beb92be

Browse files
committed
[GR-59795] Random failures in JS Test262.
PullRequest: graal/19309
2 parents f23cef1 + 5793b19 commit beb92be

File tree

10 files changed

+140
-10
lines changed

10 files changed

+140
-10
lines changed

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,11 @@ public Engine newEngine(AbstractEngineDispatch dispatch, Object receiver, boolea
946946
return engine;
947947
}
948948

949+
@Override
950+
public void processReferenceQueue() {
951+
CleanableReference.processReferenceQueue();
952+
}
953+
949954
@Override
950955
public void engineClosed(Reference<Engine> engineReference) {
951956
ENGINES.remove(engineReference);
@@ -2056,7 +2061,6 @@ private abstract static class CleanableReference<T> extends WeakReference<T> {
20562061

20572062
protected CleanableReference(T referent) {
20582063
super(referent, QUEUE);
2059-
processReferenceQueue();
20602064
}
20612065

20622066
protected abstract void clean();

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ protected APIAccess() {
160160

161161
public abstract Context newInnerContext(AbstractContextDispatch dispatch, Object receiver, Context parentContext, Engine engine);
162162

163+
public abstract void processReferenceQueue();
164+
163165
public abstract Language newLanguage(AbstractLanguageDispatch dispatch, Object receiver, Engine engine);
164166

165167
public abstract Instrument newInstrument(AbstractInstrumentDispatch dispatch, Object receiver, Engine engine);

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/PolyglotGCTest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,16 @@
8989
import java.util.Collection;
9090
import java.util.List;
9191
import java.util.Map;
92+
import java.util.concurrent.CompletionService;
9293
import java.util.concurrent.ConcurrentHashMap;
9394
import java.util.concurrent.CountDownLatch;
9495
import java.util.concurrent.ExecutionException;
96+
import java.util.concurrent.ExecutorCompletionService;
9597
import java.util.concurrent.ExecutorService;
9698
import java.util.concurrent.Executors;
9799
import java.util.concurrent.Future;
100+
import java.util.concurrent.Semaphore;
101+
import java.util.concurrent.ThreadPoolExecutor;
98102
import java.util.concurrent.TimeUnit;
99103
import java.util.concurrent.TimeoutException;
100104
import java.util.concurrent.atomic.AtomicBoolean;
@@ -864,6 +868,81 @@ public void run() {
864868
}
865869
}
866870

871+
@Test
872+
public void testGR59795() throws Exception {
873+
TruffleTestAssumptions.assumeWeakEncapsulation();
874+
runInSubprocess(() -> {
875+
List<Context> contexts = new ArrayList<>(100);
876+
// Create top-level contexts
877+
for (int i = 0; i < 100; i++) {
878+
contexts.add(newContextBuilder().build());
879+
}
880+
881+
// In each top-level context create a strongly reachable inner context
882+
for (Context context : contexts) {
883+
AbstractExecutableTestLanguage.execute(context, TestGR59795Language.class);
884+
}
885+
886+
// Make the inner context unreachable and assert that are collected
887+
List<? extends Reference<TruffleContext>> innerContextRefs = TestGR59795Language.clearInnerContexts();
888+
List<Pair<String, Reference<?>>> msgRefPairs = new ArrayList<>(innerContextRefs.size());
889+
for (Reference<?> reference : innerContextRefs) {
890+
msgRefPairs.add(Pair.create("Inner context must be collected", reference));
891+
}
892+
GCUtils.assertGc(msgRefPairs);
893+
894+
// Dispose top-level contexts in parallel
895+
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(20);
896+
CompletionService<Boolean> todo = new ExecutorCompletionService<>(executor);
897+
Semaphore threadsStarted = new Semaphore(-1 * executor.getMaximumPoolSize());
898+
CountDownLatch runClose = new CountDownLatch(1);
899+
for (Context context : contexts) {
900+
todo.submit(() -> {
901+
threadsStarted.release();
902+
runClose.await();
903+
context.close();
904+
return true;
905+
});
906+
}
907+
threadsStarted.release();
908+
try {
909+
threadsStarted.acquire();
910+
} catch (InterruptedException ie) {
911+
throw new AssertionError(ie);
912+
}
913+
runClose.countDown();
914+
for (int i = 0; i < contexts.size(); i++) {
915+
try {
916+
todo.take();
917+
} catch (InterruptedException e) {
918+
throw new AssertionError(e);
919+
}
920+
}
921+
executor.shutdown();
922+
});
923+
}
924+
925+
@Registration
926+
static final class TestGR59795Language extends AbstractPolyglotGcLanguage {
927+
928+
private static final List<TruffleContext> innerContexts = new ArrayList<>();
929+
930+
static List<? extends Reference<TruffleContext>> clearInnerContexts() {
931+
List<? extends Reference<TruffleContext>> res = TestGR59795Language.innerContexts.stream().map(WeakReference::new).toList();
932+
TestGR59795Language.innerContexts.clear();
933+
return res;
934+
}
935+
936+
@Override
937+
@TruffleBoundary
938+
protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) throws Exception {
939+
TruffleContext innerContext = env.newInnerContextBuilder().build();
940+
innerContext.initializePublic(node, TestUtils.getDefaultLanguageId(TestGR59795Language.class));
941+
innerContexts.add(innerContext);
942+
return true;
943+
}
944+
}
945+
867946
@SuppressWarnings("try")
868947
private static void assertContextGc(String message, Reference<Context> contextRef) {
869948
GCUtils.assertGc(message, contextRef);

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEngineDispatch.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ public Context createContext(Object receiver, Engine engineApi, SandboxPolicy sa
9292
long guestContextId = hostToGuest.remoteCreateContext(engine.remoteEngine, sandboxPolicy, tmpDir);
9393
HostContext context = new HostContext(engine, guestContextId, localContext);
9494
hostToGuest.registerHostContext(guestContextId, context);
95-
return polyglot.getAPIAccess().newContext(remoteContext, context, engineApi, registerInActiveContexts);
95+
Context contextApi = polyglot.getAPIAccess().newContext(remoteContext, context, engineApi, registerInActiveContexts);
96+
if (registerInActiveContexts) {
97+
polyglot.getAPIAccess().processReferenceQueue();
98+
}
99+
return contextApi;
96100
}
97101

98102
@Override

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostPolyglotDispatch.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ public Engine buildEngine(String[] permittedLanguages, SandboxPolicy sandboxPoli
7777
onlyHostLanguage, false, polyglotHostService);
7878
long remoteEngine = getHostToGuest().remoteCreateEngine(sandboxPolicy);
7979
HostEngine engine = new HostEngine(remoteEngine, localEngine);
80-
return getAPIAccess().newEngine(new HostEngineDispatch(this), engine, registerInActiveEngines);
80+
Engine engineApi = getAPIAccess().newEngine(new HostEngineDispatch(this), engine, registerInActiveEngines);
81+
if (registerInActiveEngines) {
82+
getAPIAccess().processReferenceQueue();
83+
}
84+
return engineApi;
8185
} else {
8286
return getNext().buildEngine(permittedLanguages, sandboxPolicy, out, err, in, options, allowExperimentalOptions, boundEngine, messageInterceptor, logHandler,
8387
hostLanguage,

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,8 @@ public TruffleContext createInternalContext(Object sourcePolyglotLanguageContext
11261126
impl.setCreatorTruffleContextReference(new TruffleContextCleanableReference(creatorContext, impl));
11271127
creator.context.addChildContext(impl);
11281128
}
1129+
creator.getImpl().getAPIAccess().processReferenceQueue();
1130+
TruffleContextCleanableReference.processReferenceQueue();
11291131

11301132
synchronized (impl) {
11311133
impl.initializeContextLocals();

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotContextImpl.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,10 @@ Context getContextAPI() {
614614
} else {
615615
/*
616616
* For an inner context, it may be necessary to recreate the embedder API object
617-
* when it is reintroduced from an existing TruffleContext doing host call.
617+
* when it is reintroduced from an existing TruffleContext doing host call. Avoid
618+
* processing the reference queue here, as the current thread may be holding locks.
619+
* The context's references are handled when the context or engine is created or
620+
* closed.
618621
*/
619622
result = getAPIAccess().newInnerContext(getImpl().contextDispatch, this, parent.getContextAPI(), engine.getEngineAPI());
620623
}
@@ -645,6 +648,11 @@ TruffleContext getCreatorTruffleContext() {
645648
* object for instrument notification during the close.
646649
*/
647650
truffleContext = EngineAccessor.LANGUAGE.createTruffleContext(this, parent.getCreatorTruffleContext());
651+
/*
652+
* Avoid processing the reference queue here, as the current thread may be
653+
* holding locks. The context's references are handled when the context or
654+
* engine is created or closed.
655+
*/
648656
weakCreatorTruffleContext = new TruffleContextCleanableReference(truffleContext, this);
649657
}
650658
}
@@ -3082,9 +3090,13 @@ private boolean finishClose(boolean cancelOrExitOperation, boolean notifyInstrum
30823090
* still be required to create a resulting Value, PolyglotException or ResourceLimitEvent.
30833091
* Additionally, an active cancelled context may have submitted a thread-local action to
30843092
* throw a CancelExecution exception. Closing a collected context at this place would
3085-
* execute this thread-local action, causing the exception to be lost.
3093+
* execute this thread-local action, causing the exception to be lost. Also avoid processing
3094+
* the reference queue while closing a child context as part of the parent context's
3095+
* closure. At this point, the parent's closeLock is still held. The reference queue will be
3096+
* processed later when the parent context itself is closed and the parent context's
3097+
* closeLock is no longer held.
30863098
*/
3087-
if (!active) {
3099+
if (!active && (parent == null || parent.closingThread != Thread.currentThread())) {
30883100
CompilerAsserts.neverPartOfCompilation("Direct access to weakCreatorTruffleContext");
30893101
getAPIAccess().contextClosed(weakAPI);
30903102
if (parent == null) {
@@ -3469,8 +3481,15 @@ private void finalizeContext(boolean notifyInstruments, boolean mustSucceed) {
34693481
unclosedChildContexts = getUnclosedChildContexts();
34703482
}
34713483
for (PolyglotContextImpl childCtx : unclosedChildContexts) {
3472-
if (childCtx.isActive()) {
3473-
throw new IllegalStateException("There is an active child contexts after finalizeContext!");
3484+
/*
3485+
* Another thread might be closing an unreachable child context. Closing a context
3486+
* requires it to be entered and made active. This situation is acceptable, as
3487+
* closeChildContexts will wait for the other thread to finish closing the context.
3488+
*/
3489+
synchronized (childCtx) {
3490+
if (childCtx.isActive() && !childCtx.state.isClosing()) {
3491+
throw new IllegalStateException("There is an active child context after finalizeContext!");
3492+
}
34743493
}
34753494
}
34763495
if (!unclosedChildContexts.isEmpty()) {

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,14 @@ public Context createContext(Engine engineAPI, SandboxPolicy contextSandboxPolic
19311931
}
19321932
}
19331933
}
1934+
/*
1935+
* When registerInActiveContext is false, the context is a local context decorated by its
1936+
* owning context. In this case, there's no need to process the reference queue, as it will
1937+
* be processed by the owner.
1938+
*/
1939+
if (registerInActiveContexts) {
1940+
getAPIAccess().processReferenceQueue();
1941+
}
19341942
return contextAPI;
19351943
}
19361944

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,16 @@ public Engine buildEngine(String[] permittedLanguages, SandboxPolicy sandboxPoli
352352
hostLanguageOnly,
353353
usePolyglotHostService);
354354
}
355-
return getAPIAccess().newEngine(engineDispatch, impl, registerInActiveEngines);
355+
Engine engineApi = getAPIAccess().newEngine(engineDispatch, impl, registerInActiveEngines);
356+
/*
357+
* When registerInActiveEngines is false, the engine is a local engine decorated by its
358+
* owning engine. In this case, there's no need to process the reference queue, as it
359+
* will be processed by the owner.
360+
*/
361+
if (registerInActiveEngines) {
362+
getAPIAccess().processReferenceQueue();
363+
}
364+
return engineApi;
356365
} catch (Throwable t) {
357366
if (impl == null) {
358367
throw PolyglotImpl.guestToHostException(this, t);

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/TruffleContextCleanableReference.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ final class TruffleContextCleanableReference extends WeakReference<TruffleContex
5656
TruffleContextCleanableReference(TruffleContext referent, PolyglotContextImpl polyglotContext) {
5757
super(referent, queue);
5858
this.polyglotContext = Objects.requireNonNull(polyglotContext, "PolyglotContext must be non null");
59-
processReferenceQueue();
6059
}
6160

6261
static void processReferenceQueue() {

0 commit comments

Comments
 (0)