Mercurial > hg > truffle
diff truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java @ 22121:d045a505c2b3
Asynchronous TruffleVM can be created by providing own Executor when configuring the TruffleVM.Builder
author | Jaroslav Tulach <jaroslav.tulach@oracle.com> |
---|---|
date | Wed, 02 Sep 2015 10:44:54 +0200 |
parents | fab555eab36e |
children | ac017ff52c07 |
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Mon Aug 31 18:15:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Wed Sep 02 10:44:54 2015 +0200 @@ -24,12 +24,6 @@ */ package com.oracle.truffle.api.vm; -import java.io.*; -import java.net.*; -import java.nio.file.*; -import java.util.*; -import java.util.logging.*; - import com.oracle.truffle.api.*; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.TruffleLanguage.Registration; @@ -37,6 +31,13 @@ import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.source.*; +import java.io.*; +import java.net.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.logging.*; /** * <em>Virtual machine</em> for Truffle based languages. Term virtual machine is a bit overloaded, @@ -75,6 +76,7 @@ private static final Logger LOG = Logger.getLogger(TruffleVM.class.getName()); private static final SPIAccessor SPI = new SPIAccessor(); private final Thread initThread; + private final Executor executor; private final Map<String, Language> langs; private final Reader in; private final Writer err; @@ -94,16 +96,14 @@ this.langs = null; this.handlers = null; this.globals = null; + this.executor = null; } /** * Real constructor used from the builder. - * - * @param out stdout - * @param err stderr - * @param in stdin */ - private TruffleVM(Map<String, Object> globals, Writer out, Writer err, Reader in, EventConsumer<?>[] handlers) { + private TruffleVM(Executor executor, Map<String, Object> globals, Writer out, Writer err, Reader in, EventConsumer<?>[] handlers) { + this.executor = executor; this.out = out; this.err = err; this.in = in; @@ -194,6 +194,7 @@ private Reader in; private final List<EventConsumer<?>> handlers = new ArrayList<>(); private final Map<String, Object> globals = new HashMap<>(); + private Executor executor; Builder() { } @@ -253,7 +254,7 @@ * and will take precedence over {@link TruffleLanguage#findExportedSymbol symbols exported * by languages itself}. Repeated use of <code>globalSymbol</code> is possible; later * definition of the same name overrides the previous one. - * + * * @param name name of the symbol to register * @param obj value of the object - expected to be primitive wrapper, {@link String} or * <code>TruffleObject</code> for mutual inter-operability @@ -266,6 +267,26 @@ } /** + * Provides own executor for running {@link TruffleVM} scripts. By default + * {@link TruffleVM#eval(com.oracle.truffle.api.source.Source)} and + * {@link Symbol#invoke(java.lang.Object, java.lang.Object...) are executed synchronously in + * the calling thread. Sometimes, however it is more beneficial to run them asynchronously - + * the easiest way to do so is to provide own executor when configuring the { + * @link #executor(java.util.concurrent.Executor) the builder}. The executor is expected to + * execute all {@link Runnable runnables} passed into its + * {@link Executor#execute(java.lang.Runnable)} method in the order they arrive and in a + * single (yet arbitrary) thread. + * + * @param executor the executor to use for internal execution inside the {@link #build() to + * be created} {@link TruffleVM} + * @return instance of this builder + */ + public Builder executor(Executor executor) { + this.executor = executor; + return this; + } + + /** * Creates the {@link TruffleVM Truffle virtual machine}. The configuration is taken from * values passed into configuration methods in this class. * @@ -281,7 +302,13 @@ if (in == null) { in = new InputStreamReader(System.in); } - return new TruffleVM(globals, out, err, in, handlers.toArray(new EventConsumer[0])); + Executor nonNullExecutor = executor != null ? executor : new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }; + return new TruffleVM(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0])); } } @@ -307,7 +334,7 @@ * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} */ @Deprecated - public Object eval(URI location) throws IOException { + public Symbol eval(URI location) throws IOException { checkThread(); Source s; String mimeType; @@ -329,7 +356,7 @@ URLConnection conn = url.openConnection(); mimeType = conn.getContentType(); } - TruffleLanguage<?> l = getTruffleLang(mimeType); + Language l = langs.get(mimeType); if (l == null) { throw new IOException("No language for " + location + " with MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } @@ -347,9 +374,9 @@ * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} */ @Deprecated - public Object eval(String mimeType, Reader reader) throws IOException { + public Symbol eval(String mimeType, Reader reader) throws IOException { checkThread(); - TruffleLanguage<?> l = getTruffleLang(mimeType); + Language l = langs.get(mimeType); if (l == null) { throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } @@ -367,9 +394,9 @@ * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} */ @Deprecated - public Object eval(String mimeType, String code) throws IOException { + public Symbol eval(String mimeType, String code) throws IOException { checkThread(); - TruffleLanguage<?> l = getTruffleLang(mimeType); + Language l = langs.get(mimeType); if (l == null) { throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } @@ -385,24 +412,44 @@ * @return result of an execution, possibly <code>null</code> * @throws IOException thrown to signal errors while processing the code */ - public Object eval(Source source) throws IOException { + public Symbol eval(Source source) throws IOException { String mimeType = source.getMimeType(); checkThread(); - TruffleLanguage<?> l = getTruffleLang(mimeType); + Language l = langs.get(mimeType); if (l == null) { throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } return eval(l, source); } - private Object eval(TruffleLanguage<?> l, Source s) throws IOException { - TruffleVM.findDebuggerSupport(l); - Debugger[] fillIn = {debugger}; + private Symbol eval(final Language l, final Source s) throws IOException { + final Debugger[] fillIn = {debugger}; + final Object[] result = {null, null}; + final CountDownLatch ready = new CountDownLatch(1); + final TruffleLanguage[] lang = {null}; + executor.execute(new Runnable() { + @Override + public void run() { + TruffleLanguage<?> langImpl = l.getImpl(); + lang[0] = langImpl; + TruffleVM.findDebuggerSupport(langImpl); + evalImpl(fillIn, s, result, langImpl, ready); + } + }); + exceptionCheck(result); + return new Symbol(lang[0], result, ready); + } + + private void evalImpl(Debugger[] fillIn, Source s, Object[] result, TruffleLanguage<?> l, CountDownLatch ready) { try (Closeable d = SPI.executionStart(this, fillIn, s)) { if (debugger == null) { debugger = fillIn[0]; } - return SPI.eval(l, s); + result[0] = SPI.eval(l, s); + } catch (IOException ex) { + result[1] = ex; + } finally { + ready.countDown(); } } @@ -423,34 +470,51 @@ * @param globalName the name of the symbol to find * @return found symbol or <code>null</code> if it has not been found */ - public Symbol findGlobalSymbol(String globalName) { + public Symbol findGlobalSymbol(final String globalName) { checkThread(); - TruffleLanguage<?> lang = null; - Object obj = globals.get(globalName); - Object global = null; - if (obj == null) { + final TruffleLanguage<?>[] lang = {null}; + final Object[] obj = {globals.get(globalName), null}; + final CountDownLatch ready = new CountDownLatch(1); + if (obj[0] == null) { + executor.execute(new Runnable() { + @Override + public void run() { + findGlobalSymbolImpl(obj, globalName, lang, ready); + } + }); + try { + ready.await(); + } catch (InterruptedException ex) { + LOG.log(Level.SEVERE, null, ex); + } + } else { + ready.countDown(); + } + return obj[0] == null ? null : new Symbol(lang[0], obj, ready); + } + + private void findGlobalSymbolImpl(Object[] obj, String globalName, TruffleLanguage<?>[] lang, CountDownLatch ready) { + if (obj[0] == null) { for (Language dl : langs.values()) { TruffleLanguage<?> l = dl.getImpl(); - obj = SPI.findExportedSymbol(dl.env, globalName, true); - if (obj != null) { - lang = l; - global = SPI.languageGlobal(dl.env); + obj[0] = SPI.findExportedSymbol(dl.getEnv(), globalName, true); + if (obj[0] != null) { + lang[0] = l; break; } } } - if (obj == null) { + if (obj[0] == null) { for (Language dl : langs.values()) { TruffleLanguage<?> l = dl.getImpl(); - obj = SPI.findExportedSymbol(dl.env, globalName, false); - if (obj != null) { - lang = l; - global = SPI.languageGlobal(dl.env); + obj[0] = SPI.findExportedSymbol(dl.getEnv(), globalName, false); + if (obj[0] != null) { + lang[0] = l; break; } } } - return obj == null ? null : new Symbol(lang, obj, global); + ready.countDown(); } private void checkThread() { @@ -459,12 +523,6 @@ } } - private TruffleLanguage<?> getTruffleLang(String mimeType) { - checkThread(); - Language l = langs.get(mimeType); - return l == null ? null : l.getImpl(); - } - @SuppressWarnings("all") void dispatch(Object ev) { Class type = ev.getClass(); @@ -494,20 +552,44 @@ } } + static void exceptionCheck(Object[] result) throws RuntimeException, IOException { + if (result[1] instanceof IOException) { + throw (IOException) result[1]; + } + if (result[1] instanceof RuntimeException) { + throw (RuntimeException) result[1]; + } + } + /** * Represents {@link TruffleVM#findGlobalSymbol(java.lang.String) global symbol} provided by one * of the initialized languages in {@link TruffleVM Truffle virtual machine}. */ public class Symbol { private final TruffleLanguage<?> language; - private final Object obj; - private final Object global; + private final Object[] result; + private final CountDownLatch ready; private CallTarget target; - Symbol(TruffleLanguage<?> language, Object obj, Object global) { + Symbol(TruffleLanguage<?> language, Object[] result, CountDownLatch ready) { this.language = language; - this.obj = obj; - this.global = global; + this.result = result; + this.ready = ready; + } + + /** + * Obtains the object represented by this symbol. The <em>raw</em> object can either be a + * wrapper about primitive type (e.g. {@link Number}, {@link String}, {@link Character}, + * {@link Boolean}) or a <em>TruffleObject</em> representing more complex object from a + * language. The method can return <code>null</code>. + * + * @return the object or <code>null</code> + * @throws IOException in case it is not possible to obtain the value of the object + */ + public Object get() throws IOException { + waitForSymbol(); + exceptionCheck(result); + return result[0]; } /** @@ -522,15 +604,29 @@ * @return the value returned by invoking the symbol * @throws IOException signals problem during execution */ - public Object invoke(Object thiz, Object... args) throws IOException { - checkThread(); - Debugger[] fillIn = {debugger}; - try (Closeable c = SPI.executionStart(TruffleVM.this, fillIn, null)) { + public Symbol invoke(final Object thiz, final Object... args) throws IOException { + get(); + final Debugger[] fillIn = {debugger}; + final CountDownLatch done = new CountDownLatch(1); + final Object[] res = {null, null}; + executor.execute(new Runnable() { + @Override + public void run() { + invokeImpl(fillIn, thiz, args, res, done); + } + }); + exceptionCheck(res); + return new Symbol(language, res, done); + } + + private void invokeImpl(Debugger[] fillIn, Object thiz, Object[] args, Object[] res, CountDownLatch done) { + try (final Closeable c = SPI.executionStart(TruffleVM.this, fillIn, null)) { if (debugger == null) { debugger = fillIn[0]; } List<Object> arr = new ArrayList<>(); - if (thiz == null) { + if (thiz == null && language != null) { + Object global = SPI.languageGlobal(SPI.findLanguage(TruffleVM.this, language.getClass())); if (global != null) { arr.add(global); } @@ -539,15 +635,31 @@ } arr.addAll(Arrays.asList(args)); for (;;) { - if (target == null) { - target = SPI.createCallTarget(language, obj, arr.toArray()); - } try { - return target.call(arr.toArray()); + if (target == null) { + target = SPI.createCallTarget(language, result[0], arr.toArray()); + } + res[0] = target.call(arr.toArray()); + break; } catch (SymbolInvoker.ArgumentsMishmashException ex) { target = null; } } + } catch (IOException ex) { + res[1] = ex; + } catch (RuntimeException ex) { + res[1] = ex; + } finally { + done.countDown(); + } + } + + private void waitForSymbol() throws InterruptedIOException { + checkThread(); + try { + ready.await(); + } catch (InterruptedException ex) { + throw (InterruptedIOException) new InterruptedIOException(ex.getMessage()).initCause(ex); } } } @@ -637,6 +749,13 @@ public String toString() { return "[" + getShortName() + " for " + getMimeTypes() + "]"; } + + TruffleLanguage.Env getEnv() { + if (env == null) { + env = SPI.attachEnv(TruffleVM.this, impl, out, err, in); + } + return env; + } } // end of Language // @@ -658,7 +777,7 @@ for (Map.Entry<String, Language> entrySet : langs.entrySet()) { Language languageDescription = entrySet.getValue(); if (languageClazz.isInstance(languageDescription.impl)) { - return languageDescription.env; + return languageDescription.getEnv(); } } throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); @@ -681,7 +800,7 @@ if (l == ownLang) { continue; } - Object obj = SPI.findExportedSymbol(dl.env, globalName, true); + Object obj = SPI.findExportedSymbol(dl.getEnv(), globalName, true); if (obj != null) { return obj; } @@ -691,7 +810,7 @@ if (l == ownLang) { continue; } - Object obj = SPI.findExportedSymbol(dl.env, globalName, false); + Object obj = SPI.findExportedSymbol(dl.getEnv(), globalName, false); if (obj != null) { return obj; }