# HG changeset patch # User Jaroslav Tulach # Date 1441183494 -7200 # Node ID d045a505c2b33f4bdf67886ce4e4138c6af4e613 # Parent fe5df1f36fec9a19cd9521d719a6f23b35e36d4b Asynchronous TruffleVM can be created by providing own Executor when configuring the TruffleVM.Builder diff -r fe5df1f36fec -r d045a505c2b3 truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java Mon Aug 31 18:15:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java Wed Sep 02 10:44:54 2015 +0200 @@ -31,6 +31,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.concurrent.Executors; import static org.junit.Assert.*; import org.junit.BeforeClass; import org.junit.Test; @@ -47,12 +48,12 @@ @Test public void canGetAccessToOwnLanguageInstance() throws Exception { - TruffleVM vm = TruffleVM.newVM().build(); + TruffleVM vm = TruffleVM.newVM().executor(Executors.newSingleThreadExecutor()).build(); TruffleVM.Language language = vm.getLanguages().get(L1); assertNotNull("L1 language is defined", language); Source s = Source.fromText("return nothing", "nothing").withMimeType(L1); - Object ret = vm.eval(s); + Object ret = vm.eval(s).get(); assertNull("nothing is returned", ret); Object afterInitialization = findLanguageByClass(vm); diff -r fe5df1f36fec -r d045a505c2b3 truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java Mon Aug 31 18:15:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java Wed Sep 02 10:44:54 2015 +0200 @@ -23,23 +23,21 @@ package com.oracle.truffle.api.test.vm; import com.oracle.truffle.api.source.Source; -import static org.junit.Assert.*; - -import java.io.*; - -import org.junit.*; - import static com.oracle.truffle.api.test.vm.ImplicitExplicitExportTest.L3; import com.oracle.truffle.api.vm.*; +import java.io.*; +import java.util.concurrent.Executors; +import org.junit.*; +import static org.junit.Assert.*; public class GlobalSymbolTest { @Test public void globalSymbolFoundByLanguage() throws IOException { - TruffleVM vm = TruffleVM.newVM().globalSymbol("ahoj", "42").build(); + TruffleVM vm = TruffleVM.newVM().globalSymbol("ahoj", "42").executor(Executors.newSingleThreadExecutor()).build(); // @formatter:off Object ret = vm.eval( Source.fromText("return=ahoj", "Return").withMimeType(L3) - ); + ).get(); // @formatter:on assertEquals("42", ret); } @@ -49,6 +47,6 @@ TruffleVM vm = TruffleVM.newVM().globalSymbol("ahoj", "42").build(); TruffleVM.Symbol ret = vm.findGlobalSymbol("ahoj"); assertNotNull("Symbol found", ret); - assertEquals("42", ret.invoke(null)); + assertEquals("42", ret.get()); } } diff -r fe5df1f36fec -r d045a505c2b3 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 Mon Aug 31 18:15:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Wed Sep 02 10:44:54 2015 +0200 @@ -22,13 +22,6 @@ */ package com.oracle.truffle.api.test.vm; -import static org.junit.Assert.*; - -import java.io.*; -import java.util.*; - -import org.junit.*; - import com.oracle.truffle.api.*; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.debug.*; @@ -37,25 +30,37 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.vm.*; +import java.io.*; +import java.util.*; +import java.util.concurrent.Executors; +import org.junit.*; +import static org.junit.Assert.*; public class ImplicitExplicitExportTest { + private static Thread mainThread; private TruffleVM vm; @Before public void initializeVM() { - vm = TruffleVM.newVM().build(); + mainThread = Thread.currentThread(); + vm = TruffleVM.newVM().executor(Executors.newSingleThreadExecutor()).build(); assertTrue("Found " + L1 + " language", vm.getLanguages().containsKey(L1)); assertTrue("Found " + L2 + " language", vm.getLanguages().containsKey(L2)); assertTrue("Found " + L3 + " language", vm.getLanguages().containsKey(L3)); } + @After + public void cleanThread() { + mainThread = null; + } + @Test public void explicitExportFound() throws IOException { // @formatter:off vm.eval(Source.fromText("explicit.ahoj=42", "Fourty two").withMimeType(L1)); Object ret = vm.eval( Source.fromText("return=ahoj", "Return").withMimeType(L3) - ); + ).get(); // @formatter:on assertEquals("42", ret); } @@ -68,7 +73,7 @@ ); Object ret = vm.eval( Source.fromText("return=ahoj", "Return").withMimeType(L3) - ); + ).get(); // @formatter:on assertEquals("42", ret); } @@ -84,10 +89,10 @@ ); Object ret = vm.eval( Source.fromText("return=ahoj", "Return").withMimeType(L3) - ); + ).get(); // @formatter:on assertEquals("Explicit import from L2 is used", "43", ret); - assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").invoke(null)); + assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").get()); } @Test @@ -101,10 +106,10 @@ ); Object ret = vm.eval( Source.fromText("return=ahoj", "Return").withMimeType(L3) - ); + ).get(); // @formatter:on assertEquals("Explicit import from L2 is used", "43", ret); - assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").invoke(null)); + assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").invoke(null).get()); } private static final class Ctx { @@ -121,6 +126,9 @@ @Override protected Ctx createContext(Env env) { + if (mainThread != null) { + assertNotEquals("Should run asynchronously", Thread.currentThread(), mainThread); + } return new Ctx(env); } @@ -164,6 +172,7 @@ } private Object importExport(Source code) { + assertNotEquals("Should run asynchronously", Thread.currentThread(), mainThread); final Node node = createFindContextNode(); Ctx ctx = findContext(node); Properties p = new Properties(); diff -r fe5df1f36fec -r d045a505c2b3 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java --- 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.*; /** * Virtual machine 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 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 globals, Writer out, Writer err, Reader in, EventConsumer[] handlers) { + private TruffleVM(Executor executor, Map 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> handlers = new ArrayList<>(); private final Map 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 globalSymbol 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 * TruffleObject 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 null * @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 null 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 raw object can either be a + * wrapper about primitive type (e.g. {@link Number}, {@link String}, {@link Character}, + * {@link Boolean}) or a TruffleObject representing more complex object from a + * language. The method can return null. + * + * @return the object or null + * @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 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 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; } diff -r fe5df1f36fec -r d045a505c2b3 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Mon Aug 31 18:15:27 2015 -0700 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Wed Sep 02 10:44:54 2015 +0200 @@ -245,7 +245,7 @@ Source src = Source.fromFileName(path.toString()); /* Parse the SL source file. */ - Object result = context.eval(src.withMimeType("application/x-sl")); + Object result = context.eval(src.withMimeType("application/x-sl")).get(); if (result != null) { out.println(result); } @@ -270,7 +270,7 @@ long start = System.nanoTime(); /* Call the main entry point, without any arguments. */ try { - result = main.invoke(null); + result = main.invoke(null).get(); if (result != null) { out.println(result); } diff -r fe5df1f36fec -r d045a505c2b3 truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java --- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java Mon Aug 31 18:15:27 2015 -0700 +++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java Wed Sep 02 10:44:54 2015 +0200 @@ -139,7 +139,7 @@ public void testFortyTwo() throws Exception { TruffleVM.Symbol fourtyTwo = findGlobalSymbol(fourtyTwo()); - Object res = fourtyTwo.invoke(null); + Object res = fourtyTwo.invoke(null).get(); assert res instanceof Number : "should yield a number, but was: " + res; @@ -155,7 +155,7 @@ } TruffleVM.Symbol retNull = findGlobalSymbol(returnsNull()); - Object res = retNull.invoke(null); + Object res = retNull.invoke(null).get(); assertNull("Should yield real Java null", res); } @@ -168,7 +168,7 @@ TruffleVM.Symbol plus = findGlobalSymbol(plusInt()); - Object res = plus.invoke(null, a, b); + Object res = plus.invoke(null, a, b).get(); assert res instanceof Number : "+ on two ints should yield a number, but was: " + res; @@ -181,7 +181,7 @@ public void testInvalidTestMethod() throws Exception { String mime = mimeType(); String code = invalidCode(); - Object ret = vm().eval(Source.fromText(code, "Invalid code").withMimeType(mime)); + Object ret = vm().eval(Source.fromText(code, "Invalid code").withMimeType(mime)).get(); fail("Should yield IOException, but returned " + ret); } @@ -189,7 +189,7 @@ public void testMaxOrMinValue() throws Exception { TruffleVM.Symbol apply = findGlobalSymbol(applyNumbers()); - Object res = apply.invoke(null, new MaxMinObject(true)); + Object res = apply.invoke(null, new MaxMinObject(true)).get(); assert res instanceof Number : "result should be a number: " + res; @@ -202,7 +202,7 @@ public void testMaxOrMinValue2() throws Exception { TruffleVM.Symbol apply = findGlobalSymbol(applyNumbers()); - Object res = apply.invoke(null, new MaxMinObject(false)); + Object res = apply.invoke(null, new MaxMinObject(false)).get(); assert res instanceof Number : "result should be a number: " + res; @@ -228,12 +228,12 @@ for (int i = 0; i < 10; i++) { int quantum = r.nextInt(10); for (int j = 0; j < quantum; j++) { - Object res = count1.invoke(null); + Object res = count1.invoke(null).get(); assert res instanceof Number : "expecting number: " + res; assert ((Number) res).intValue() == ++prev1 : "expecting " + prev1 + " but was " + res; } for (int j = 0; j < quantum; j++) { - Object res = count2.invoke(null); + Object res = count2.invoke(null).get(); assert res instanceof Number : "expecting number: " + res; assert ((Number) res).intValue() == ++prev2 : "expecting " + prev2 + " but was " + res; }