changeset 22350:b1c71f0c1a85

Unifying the executor access into single ComputeInExecutor class and removing the need for CountDownLatch
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Thu, 05 Nov 2015 18:42:36 +0100
parents 522155ade3bf
children 536c5b85fe1d 9a6f0c689576
files truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/ComputeInExecutor.java truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java
diffstat 2 files changed, 233 insertions(+), 172 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/ComputeInExecutor.java	Thu Nov 05 18:42:36 2015 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.vm;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.concurrent.Executor;
+
+abstract class ComputeInExecutor<R> implements Runnable {
+    private final Executor executor;
+    private R result;
+    private Throwable exception;
+    private boolean started;
+    private boolean done;
+
+    protected ComputeInExecutor(Executor executor) {
+        this.executor = executor;
+    }
+
+    protected abstract R compute() throws IOException;
+
+    public final R get() throws IOException {
+        perform();
+        if (executor != null) {
+            waitForDone();
+        }
+        exceptionCheck();
+        return result;
+    }
+
+    private void waitForDone() throws InterruptedIOException {
+        synchronized (this) {
+            while (!done) {
+                try {
+                    wait();
+                } catch (InterruptedException ex) {
+                    throw new InterruptedIOException(ex.getMessage());
+                }
+            }
+        }
+    }
+
+    private void exceptionCheck() throws IOException, RuntimeException {
+        if (exception instanceof IOException) {
+            throw (IOException) exception;
+        }
+        if (exception instanceof RuntimeException) {
+            throw (RuntimeException) exception;
+        }
+        if (exception != null) {
+            throw new RuntimeException(exception);
+        }
+    }
+
+    public final void perform() throws IOException {
+        if (started) {
+            return;
+        }
+        started = true;
+        if (executor == null) {
+            run();
+        } else {
+            executor.execute(this);
+        }
+        exceptionCheck();
+    }
+
+    @Override
+    public final void run() {
+        try {
+            result = compute();
+        } catch (Exception ex) {
+            exception = ex;
+        } finally {
+            if (executor != null) {
+                synchronized (this) {
+                    done = true;
+                    notifyAll();
+                }
+            } else {
+                done = true;
+            }
+        }
+    }
+
+    @Override
+    public final String toString() {
+        return "value=" + result + ",exception=" + exception + ",computed=" + done;
+    }
+}
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Thu Nov 05 17:11:51 2015 +0100
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Thu Nov 05 18:42:36 2015 +0100
@@ -27,7 +27,6 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InterruptedIOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -38,9 +37,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import com.oracle.truffle.api.CallTarget;
@@ -64,6 +61,7 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
+import java.util.logging.Level;
 
 /**
  * Gate way into the world of {@link TruffleLanguage Truffle languages}. {@link #buildNew()
@@ -103,13 +101,6 @@
 @SuppressWarnings("rawtypes")
 public class PolyglotEngine {
     static final boolean JAVA_INTEROP_ENABLED = !TruffleOptions.AOT;
-    private static final Executor DIRECT_EXECUTOR = new Executor() {
-        @Override
-        public void execute(Runnable command) {
-            command.run();
-        }
-    };
-
     static final Logger LOG = Logger.getLogger(PolyglotEngine.class.getName());
     private static final SPIAccessor SPI = new SPIAccessor();
     private final Thread initThread;
@@ -342,8 +333,7 @@
             if (in == null) {
                 in = System.in;
             }
-            Executor nonNullExecutor = executor != null ? executor : DIRECT_EXECUTOR;
-            return new PolyglotEngine(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0]));
+            return new PolyglotEngine(executor, globals, out, err, in, handlers.toArray(new EventConsumer[0]));
         }
     }
 
@@ -388,9 +378,9 @@
     public void dispose() {
         checkThread();
         disposed = true;
-        executor.execute(new Runnable() {
+        ComputeInExecutor<Void> compute = new ComputeInExecutor<Void>(executor) {
             @Override
-            public void run() {
+            protected Void compute() throws IOException {
                 for (Language language : getLanguages().values()) {
                     TruffleLanguage<?> impl = language.getImpl(false);
                     if (impl != null) {
@@ -401,26 +391,26 @@
                         }
                     }
                 }
+                return null;
             }
-        });
+        };
+        try {
+            compute.perform();
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        }
     }
 
     private Value eval(final Language l, final Source s) throws IOException {
-        final Object[] result = {null, null};
-        final CountDownLatch ready = new CountDownLatch(1);
         final TruffleLanguage[] lang = {null};
-        executor.execute(new Runnable() {
+        ComputeInExecutor<Object> compute = new ComputeInExecutor<Object>(executor) {
             @Override
-            public void run() {
-                evalImpl(lang, s, result, l, ready);
+            protected Object compute() throws IOException {
+                return evalImpl(lang, s, l);
             }
-        });
-        exceptionCheck(result);
-        return createValue(lang, result, ready);
-    }
-
-    Value createValue(TruffleLanguage[] lang, Object[] result, CountDownLatch ready) {
-        return new Value(lang, result, ready);
+        };
+        compute.perform();
+        return new Value(lang, compute);
     }
 
     Language createLanguage(Map.Entry<String, LanguageCache> en) {
@@ -428,62 +418,48 @@
     }
 
     @SuppressWarnings("try")
-    private void evalImpl(TruffleLanguage<?>[] fillLang, Source s, Object[] result, Language l, CountDownLatch ready) {
+    private Object evalImpl(TruffleLanguage<?>[] fillLang, Source s, Language l) throws IOException {
         try (Closeable d = SPI.executionStart(this, -1, debugger, s)) {
             TruffleLanguage<?> langImpl = l.getImpl(true);
             fillLang[0] = langImpl;
-            result[0] = SPI.eval(langImpl, s);
-        } catch (IOException ex) {
-            result[1] = ex;
-        } finally {
-            ready.countDown();
+            return SPI.eval(langImpl, s);
         }
     }
 
     @SuppressWarnings("try")
     final Object invokeForeign(final Node foreignNode, VirtualFrame frame, final TruffleObject receiver) throws IOException {
-        final Object[] res = {null, null};
-        if (executor == DIRECT_EXECUTOR) {
+        Object res;
+        if (executor == null) {
             try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) {
                 final Object[] args = ForeignAccess.getArguments(frame).toArray();
-                res[0] = ForeignAccess.execute(foreignNode, frame, receiver, args);
+                res = ForeignAccess.execute(foreignNode, frame, receiver, args);
             }
         } else {
-            invokeForeignOnExecutor(foreignNode, frame, receiver, res);
+            res = invokeForeignOnExecutor(foreignNode, frame, receiver);
         }
-        exceptionCheck(res);
-        if (res[0] instanceof TruffleObject) {
-            return new EngineTruffleObject(this, (TruffleObject) res[0]);
+        if (res instanceof TruffleObject) {
+            return new EngineTruffleObject(this, (TruffleObject) res);
         } else {
-            return res[0];
+            return res;
         }
     }
 
     @TruffleBoundary
-    private void invokeForeignOnExecutor(final Node foreignNode, VirtualFrame frame, final TruffleObject receiver, final Object[] res) throws IOException {
+    private Object invokeForeignOnExecutor(final Node foreignNode, VirtualFrame frame, final TruffleObject receiver) throws IOException {
         final MaterializedFrame materialized = frame.materialize();
-        final CountDownLatch computed = new CountDownLatch(1);
-        executor.execute(new Runnable() {
+        ComputeInExecutor<Object> compute = new ComputeInExecutor<Object>(executor) {
             @SuppressWarnings("try")
             @Override
-            public void run() {
+            protected Object compute() throws IOException {
                 try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) {
                     final Object[] args = ForeignAccess.getArguments(materialized).toArray();
                     RootNode node = SymbolInvokerImpl.createTemporaryRoot(TruffleLanguage.class, foreignNode, receiver, args.length);
                     final CallTarget target = Truffle.getRuntime().createCallTarget(node);
-                    res[0] = target.call(args);
-                } catch (Exception ex) {
-                    res[1] = ex;
-                } finally {
-                    computed.countDown();
+                    return target.call(args);
                 }
             }
-        });
-        try {
-            computed.await();
-        } catch (InterruptedException ex) {
-            throw new InterruptedIOException(ex.getMessage());
-        }
+        };
+        return compute.get();
     }
 
     /**
@@ -506,54 +482,48 @@
     public Value findGlobalSymbol(final String globalName) {
         checkThread();
         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);
+        ComputeInExecutor<Object> compute = new ComputeInExecutor<Object>(executor) {
+            @Override
+            protected Object compute() throws IOException {
+                Object obj = globals.get(globalName);
+                if (obj == null) {
+                    for (Language dl : langs.values()) {
+                        TruffleLanguage.Env env = dl.getEnv(false);
+                        if (env == null) {
+                            continue;
+                        }
+                        obj = SPI.findExportedSymbol(env, globalName, true);
+                        if (obj != null) {
+                            lang[0] = dl.getImpl(true);
+                            break;
+                        }
+                    }
                 }
-            });
-            try {
-                ready.await();
-            } catch (InterruptedException ex) {
-                LOG.log(Level.SEVERE, null, ex);
+                if (obj == null) {
+                    for (Language dl : langs.values()) {
+                        TruffleLanguage.Env env = dl.getEnv(false);
+                        if (env == null) {
+                            continue;
+                        }
+                        obj = SPI.findExportedSymbol(env, globalName, true);
+                        if (obj != null) {
+                            lang[0] = dl.getImpl(true);
+                            break;
+                        }
+                    }
+                }
+                return obj;
             }
-        } else {
-            ready.countDown();
+        };
+        try {
+            compute.perform();
+            if (compute.get() == null) {
+                return null;
+            }
+        } catch (IOException ex) {
+            // OK, go on
         }
-        return obj[0] == null ? null : createValue(lang, obj, ready);
-    }
-
-    private void findGlobalSymbolImpl(Object[] obj, String globalName, TruffleLanguage<?>[] lang, CountDownLatch ready) {
-        if (obj[0] == null) {
-            for (Language dl : langs.values()) {
-                TruffleLanguage.Env env = dl.getEnv(false);
-                if (env == null) {
-                    continue;
-                }
-                obj[0] = SPI.findExportedSymbol(env, globalName, true);
-                if (obj[0] != null) {
-                    lang[0] = dl.getImpl(true);
-                    break;
-                }
-            }
-        }
-        if (obj[0] == null) {
-            for (Language dl : langs.values()) {
-                TruffleLanguage.Env env = dl.getEnv(false);
-                if (env == null) {
-                    continue;
-                }
-                obj[0] = SPI.findExportedSymbol(env, globalName, true);
-                if (obj[0] != null) {
-                    lang[0] = dl.getImpl(true);
-                    break;
-                }
-            }
-        }
-        ready.countDown();
+        return new Value(lang, compute);
     }
 
     private void checkThread() {
@@ -594,15 +564,6 @@
         }
     }
 
-    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];
-        }
-    }
-
     /**
      * A future value wrapper. A user level wrapper around values returned by evaluation of various
      * {@link PolyglotEngine} functions like
@@ -616,14 +577,22 @@
      */
     public class Value {
         private final TruffleLanguage<?>[] language;
-        private final Object[] result;
-        private final CountDownLatch ready;
+        private final ComputeInExecutor<Object> compute;
         private CallTarget target;
 
-        Value(TruffleLanguage<?>[] language, Object[] result, CountDownLatch ready) {
+        Value(TruffleLanguage<?>[] language, ComputeInExecutor<Object> compute) {
+            this.language = language;
+            this.compute = compute;
+        }
+
+        Value(TruffleLanguage<?>[] language, final Object value) {
             this.language = language;
-            this.result = result;
-            this.ready = ready;
+            this.compute = new ComputeInExecutor<Object>(null) {
+                @Override
+                protected Object compute() throws IOException {
+                    return value;
+                }
+            };
         }
 
         /**
@@ -636,12 +605,11 @@
          * @throws IOException in case it is not possible to obtain the value of the object
          */
         public Object get() throws IOException {
-            waitForSymbol();
-            exceptionCheck(result);
-            if (result[0] instanceof TruffleObject) {
-                return new EngineTruffleObject(PolyglotEngine.this, (TruffleObject) result[0]);
+            Object result = waitForSymbol();
+            if (result instanceof TruffleObject) {
+                return new EngineTruffleObject(PolyglotEngine.this, (TruffleObject) result);
             } else {
-                return result[0];
+                return result;
             }
         }
 
@@ -700,67 +668,48 @@
          */
         public Value invoke(final Object thiz, final Object... args) throws IOException {
             get();
-            final CountDownLatch done = new CountDownLatch(1);
-            final Object[] res = {null, null};
-            executor.execute(new Runnable() {
+            ComputeInExecutor<Object> invokeCompute = new ComputeInExecutor<Object>(executor) {
+                @SuppressWarnings("try")
                 @Override
-                public void run() {
-                    invokeImpl(thiz, args, res, done);
-                }
-            });
-            exceptionCheck(res);
-            return createValue(language, res, done);
-        }
-
-        @SuppressWarnings("try")
-        private void invokeImpl(Object thiz, Object[] args, Object[] res, CountDownLatch done) {
-            try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) {
-                List<Object> arr = new ArrayList<>();
-                if (thiz == null) {
-                    if (language[0] != null) {
-                        Object global = SPI.languageGlobal(SPI.findLanguage(PolyglotEngine.this, language[0].getClass()));
-                        if (global != null) {
-                            arr.add(global);
+                protected Object compute() throws IOException {
+                    try (final Closeable c = SPI.executionStart(PolyglotEngine.this, -1, debugger, null)) {
+                        List<Object> arr = new ArrayList<>();
+                        if (thiz == null) {
+                            if (language[0] != null) {
+                                Object global = SPI.languageGlobal(SPI.findLanguage(PolyglotEngine.this, language[0].getClass()));
+                                if (global != null) {
+                                    arr.add(global);
+                                }
+                            }
+                        } else {
+                            arr.add(thiz);
+                        }
+                        arr.addAll(Arrays.asList(args));
+                        for (;;) {
+                            try {
+                                if (target == null) {
+                                    target = SymbolInvokerImpl.createCallTarget(language[0], compute.get(), arr.toArray());
+                                }
+                                return target.call(arr.toArray());
+                            } catch (ArgumentsMishmashException ex) {
+                                target = null;
+                            }
                         }
                     }
-                } else {
-                    arr.add(thiz);
                 }
-                arr.addAll(Arrays.asList(args));
-                for (;;) {
-                    try {
-                        if (target == null) {
-                            target = SymbolInvokerImpl.createCallTarget(language[0], result[0], arr.toArray());
-                        }
-                        res[0] = target.call(arr.toArray());
-                        break;
-                    } catch (ArgumentsMishmashException ex) {
-                        target = null;
-                    }
-                }
-            } catch (IOException ex) {
-                res[1] = ex;
-            } catch (RuntimeException ex) {
-                res[1] = ex;
-            } finally {
-                done.countDown();
-            }
+            };
+            invokeCompute.perform();
+            return new Value(language, invokeCompute);
         }
 
-        private void waitForSymbol() throws InterruptedIOException {
+        private Object waitForSymbol() throws IOException {
             checkThread();
-            try {
-                if (ready != null) {
-                    ready.await();
-                }
-            } catch (InterruptedException ex) {
-                throw (InterruptedIOException) new InterruptedIOException(ex.getMessage()).initCause(ex);
-            }
+            return compute.get();
         }
 
         @Override
         public String toString() {
-            return "PolyglotEngine.Value[value=" + result[0] + ",exception=" + result[1] + ",computed=" + (ready.getCount() == 0) + "]";
+            return "PolyglotEngine.Value[" + compute + "]";
         }
     }
 
@@ -834,8 +783,8 @@
         public Value getGlobalObject() {
             checkThread();
 
-            Object[] res = {SPI.languageGlobal(getEnv(true)), null};
-            return res[0] == null ? null : new Value(new TruffleLanguage[]{info.getImpl(true)}, res, null);
+            Object res = SPI.languageGlobal(getEnv(true));
+            return res == null ? null : new Value(new TruffleLanguage[]{info.getImpl(true)}, res);
         }
 
         TruffleLanguage<?> getImpl(boolean create) {