# HG changeset patch # User Jaroslav Tulach # Date 1443008541 -7200 # Node ID 2e7352f9ffa8d9ac48215b23a0cebb23173e50d8 # Parent 21863e2a1d344829870b1d8e748151e6029d3225# Parent b50f3bc4ed373a02abdc9b39f794474aefd773f7 Merging disposeContext into main development line diff -r 21863e2a1d34 -r 2e7352f9ffa8 truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ExceptionDuringParsingTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ExceptionDuringParsingTest.java Tue Sep 22 15:49:49 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ExceptionDuringParsingTest.java Wed Sep 23 13:42:21 2015 +0200 @@ -24,28 +24,64 @@ import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.test.vm.ImplicitExplicitExportTest.Ctx; import static com.oracle.truffle.api.test.vm.ImplicitExplicitExportTest.L1; import com.oracle.truffle.api.vm.PolyglotEngine; import java.io.IOException; +import org.junit.After; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.junit.Before; import org.junit.Test; public class ExceptionDuringParsingTest { public static Accessor API; + @Before + @After + public void cleanTheSet() { + Ctx.disposed.clear(); + } + @Test public void canGetAccessToOwnLanguageInstance() throws Exception { PolyglotEngine vm = PolyglotEngine.buildNew().build(); PolyglotEngine.Language language = vm.getLanguages().get(L1); assertNotNull("L1 language is defined", language); + final Source src = Source.fromText("parse=No, no, no!", "Fail on parsing").withMimeType(L1); try { - vm.eval(Source.fromText("parse=No, no, no!", "Fail on parsing").withMimeType(L1)); + vm.eval(src); fail("Exception thrown"); } catch (IOException ex) { assertEquals(ex.getMessage(), "No, no, no!"); } + + assertEquals("No dispose yet", 0, Ctx.disposed.size()); + + vm.dispose(); + + assertEquals("One context disposed", 1, Ctx.disposed.size()); + + try { + vm.eval(src); + fail("Should throw an exception"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage(), ex.getMessage().contains("disposed")); + } + try { + vm.findGlobalSymbol("nothing"); + fail("Should throw an exception"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage(), ex.getMessage().contains("disposed")); + } + try { + vm.dispose(); + fail("Should throw an exception"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage(), ex.getMessage().contains("disposed")); + } } } diff -r 21863e2a1d34 -r 2e7352f9ffa8 truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Tue Sep 22 15:49:49 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Wed Sep 23 13:42:21 2015 +0200 @@ -36,8 +36,10 @@ import java.io.Reader; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.concurrent.Executors; import org.junit.After; import static org.junit.Assert.assertEquals; @@ -122,7 +124,9 @@ assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").invoke(null).get()); } - private static final class Ctx { + static final class Ctx { + static final Set disposed = new HashSet<>(); + final Map explicit = new HashMap<>(); final Map implicit = new HashMap<>(); final Env env; @@ -130,6 +134,10 @@ public Ctx(Env env) { this.env = env; } + + void dispose() { + disposed.add(this); + } } private abstract static class AbstractExportImportLanguage extends TruffleLanguage { @@ -143,6 +151,11 @@ } @Override + protected void disposeContext(Ctx context) { + context.dispose(); + } + + @Override protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException { if (code.getCode().startsWith("parse=")) { throw new IOException(code.getCode().substring(6)); diff -r 21863e2a1d34 -r 2e7352f9ffa8 truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Tue Sep 22 15:49:49 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Wed Sep 23 13:42:21 2015 +0200 @@ -112,6 +112,7 @@ private final EventConsumer[] handlers; private final Map globals; private Debugger debugger; + private boolean disposed; /** * Private & temporary only constructor. @@ -447,6 +448,35 @@ return eval(l, source); } + /** + * Dispose instance of this engine. A user can explicitly + * {@link TruffleLanguage#disposeContext(java.lang.Object) dispose all resources} allocated by + * the languages active in this engine, when it is known the system is not going to be used in + * the future. + *

+ * Calling any other method of this class after the dispose has been done yields an + * {@link IllegalStateException}. + */ + public void dispose() { + checkThread(); + disposed = true; + executor.execute(new Runnable() { + @Override + public void run() { + for (Language language : getLanguages().values()) { + TruffleLanguage impl = language.getImpl(false); + if (impl != null) { + try { + SPI.dispose(impl, language.getEnv(true)); + } catch (Exception | Error ex) { + LOG.log(Level.SEVERE, "Error disposing " + impl, ex); + } + } + } + } + }); + } + private Value eval(final Language l, final Source s) throws IOException { final Debugger[] fillIn = {debugger}; final Object[] result = {null, null}; @@ -561,6 +591,9 @@ if (initThread != Thread.currentThread()) { throw new IllegalStateException("PolyglotEngine created on " + initThread.getName() + " but used on " + Thread.currentThread().getName()); } + if (disposed) { + throw new IllegalStateException("Engine has already been disposed"); + } } @SuppressWarnings("unchecked") @@ -952,5 +985,10 @@ PolyglotEngine vm = (PolyglotEngine) obj; vm.dispatch(event); } + + @Override + protected void dispose(TruffleLanguage impl, TruffleLanguage.Env env) { + super.dispose(impl, env); + } } // end of SPIAccessor } diff -r 21863e2a1d34 -r 2e7352f9ffa8 truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java diff -r 21863e2a1d34 -r 2e7352f9ffa8 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Tue Sep 22 15:49:49 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Wed Sep 23 13:42:21 2015 +0200 @@ -118,6 +118,19 @@ protected abstract C createContext(Env env); /** + * Disposes context created by + * {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)}. A language can be asked + * by its user to clean-up. In such case the language is supposed to dispose any + * resources acquired before and dispose the context - e.g. render it + * useless for any future calls. + * + * @param context the context {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env) + * created by the language} + */ + protected void disposeContext(C context) { + } + + /** * Parses the provided source and generates appropriate AST. The parsing should execute no user * code, it should only create the {@link Node} tree to represent the source. The parsing may be * performed in a context (specified as another {@link Node}) or without context. The @@ -238,6 +251,10 @@ Object getLanguageGlobal() { return lang.getLanguageGlobal(ctx); } + + void dispose() { + lang.disposeContext(ctx); + } } /** @@ -390,6 +407,12 @@ protected DebugSupportProvider getDebugSupport(TruffleLanguage l) { return l.getDebugSupport(); } + + @Override + protected void dispose(TruffleLanguage impl, Env env) { + assert impl == env.langCtx.lang; + env.langCtx.dispose(); + } } } diff -r 21863e2a1d34 -r 2e7352f9ffa8 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Tue Sep 22 15:49:49 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Wed Sep 23 13:42:21 2015 +0200 @@ -237,4 +237,8 @@ protected TruffleLanguage findLanguage(Env env) { return API.findLanguage(env); } + + protected void dispose(TruffleLanguage impl, Env env) { + API.dispose(impl, env); + } }