package com.oracle.truffle.api.test.polyglot;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import org.graalvm.polyglot.Context;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class MultiThreadedCloseTest extends AbstractPolyglotTest {
public MultiThreadedCloseTest() {
enterContext = false;
}
@Test
public void testWithExecutor() {
setupEnv(Context.newBuilder().allowCreateThread(true).build(), new CloseLanguage() {
@Override
protected void initializeContext(CloseContext ctx) throws Exception {
ctx.executor = Executors.newCachedThreadPool((r) -> {
return ctx.registerThread(ctx.env.createThread(r, null, ctx.group));
});
ctx.executor.submit(() -> {
ctx.env.parseInternal(Source.newBuilder(ProxyLanguage.ID, "", "test").build());
});
}
@Override
protected void finalizeContext(CloseContext ctx) {
ctx.executor.shutdownNow();
assertTrue(ctx.executor.isShutdown());
try {
ctx.joinThreads();
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
});
context.eval(ProxyLanguage.ID, "");
}
@Test
public void testWithThreads() {
setupEnv(Context.newBuilder().allowCreateThread(true).build(), new CloseLanguage() {
@Override
protected void initializeContext(CloseContext ctx) throws Exception {
ctx.registerThread(ctx.env.createThread(() -> {
ctx.env.parseInternal(Source.newBuilder(ProxyLanguage.ID, "", "test").build());
}, null, ctx.group)).start();
}
@Override
protected void finalizeContext(CloseContext ctx) {
try {
ctx.joinThreads();
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
});
context.eval(ProxyLanguage.ID, "");
}
private static class CloseContext extends ProxyLanguage.LanguageContext {
final AtomicReference<Collection<Thread>> createdThreads;
final ThreadGroup group;
ExecutorService executor;
CloseContext(Env env) {
super(env);
this.createdThreads = new AtomicReference<>(Collections.synchronizedList(new ArrayList<>()));
this.group = new ThreadGroup(getClass().getSimpleName());
}
Thread registerThread(Thread thread) {
Collection<Thread> into = createdThreads.get();
if (into == null) {
throw new IllegalStateException("Creating a Thread after finalizeContext");
}
into.add(thread);
return thread;
}
void joinThreads() throws InterruptedException {
Collection<Thread> toJoin = createdThreads.getAndSet(null);
if (toJoin == null) {
return;
}
for (Thread t : toJoin) {
t.join();
}
}
}
private abstract static class CloseLanguage extends ProxyLanguage {
@Override
protected final boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) {
return true;
}
@Override
protected LanguageContext createContext(Env env) {
super.createContext(env);
return new CloseContext(env);
}
@Override
protected final void initializeContext(LanguageContext context) throws Exception {
initializeContext((CloseContext) context);
}
protected abstract void initializeContext(CloseContext context) throws Exception;
@Override
protected void finalizeContext(LanguageContext context) {
finalizeContext((CloseContext) context);
}
protected abstract void finalizeContext(CloseContext context);
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(true));
}
}
}