From e6e52d1be5f4a4855c60500b57f5c7566bb37e5b Mon Sep 17 00:00:00 2001 From: Steve Hannah Date: Wed, 4 Feb 2015 13:55:02 -0800 Subject: [PATCH] Added preliminary support for keeping track of which thread is currently running. This implementation simply overrides setTimeout() to keep track of the thread that is running. It restores it to the main thread after a thread finishes running. May need to override other async methods e.g. XMLHTTPRequest, but not sure yet. This change may be sufficient if all of our async methods meant to emulate threads use the setTimeout construction. --- .../org/teavm/classlib/java/lang/TThread.java | 35 +++++++++++++++---- .../java/lang/ThreadNativeGenerator.java | 1 - .../src/main/java/org/teavm/vm/TeaVM.java | 27 ++++++++++++++ .../org/teavm/samples/async/AsyncProgram.java | 8 +++-- 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java index 8b7715d38..77d8c6c1e 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThread.java @@ -24,7 +24,11 @@ import org.teavm.runtime.Async; * @author Alexey Andreev */ public class TThread extends TObject implements TRunnable { - private static TThread currentThread = new TThread(TString.wrap("main")); + private static TThread mainThread = new TThread(TString.wrap("main")); + private static TThread currentThread = mainThread; + private static long nextId = 1; + private static int activeCount = 1; + private long id; private TString name; private TRunnable target; @@ -33,16 +37,17 @@ public class TThread extends TObject implements TRunnable { } public TThread(TString name) { - this(name, null); + this(null, name); } public TThread(TRunnable target) { - this(null, target); + this(target, null ); } - public TThread(TString name, TRunnable target) { + public TThread(TRunnable target, TString name ) { this.name = name; this.target = target; + id=nextId++; } @PluggableDependency(ThreadNativeGenerator.class) @@ -50,7 +55,23 @@ public class TThread extends TObject implements TRunnable { public native void start(); private static void launch(TThread thread) { - thread.run(); + try { + activeCount++; + setCurrentThread(thread); + thread.run(); + } finally { + activeCount--; + setCurrentThread(mainThread); + } + + + } + + private static void setCurrentThread(TThread thread){ + currentThread = thread; + } + private static TThread getMainThread(){ + return mainThread; } @Override @@ -84,11 +105,11 @@ public class TThread extends TObject implements TRunnable { } public static int activeCount() { - return 1; + return activeCount; } public long getId() { - return 1; + return id; } public static boolean holdsLock(@SuppressWarnings("unused") TObject obj) { diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java index 0537343d6..7a12c7c2d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ThreadNativeGenerator.java @@ -71,7 +71,6 @@ public class ThreadNativeGenerator implements Generator, DependencyPlugin { private void generateStart(GeneratorContext context, SourceWriter writer) throws IOException { String obj = context.getParameterName(0); - writer.append("setTimeout(function() { $rt_rootInvocationAdapter(").appendMethodBody(launchRef).append(")(") .append(obj).append(");},0);").softNewLine(); } diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 7a03aa30b..719fff4d3 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -344,6 +344,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository { internDep.use(); dependencyChecker.linkMethod(new MethodReference(String.class, "length", int.class), null).use(); dependencyChecker.linkMethod(new MethodReference(Object.class, "clone", Object.class), null).use(); + dependencyChecker.linkMethod(new MethodReference(Thread.class, "currentThread", Thread.class), null).use(); + dependencyChecker.linkMethod(new MethodReference(Thread.class, "getMainThread", Thread.class), null).use(); + dependencyChecker.linkMethod( + new MethodReference(Thread.class, "setCurrentThread", Thread.class, void.class), null).use(); MethodDependency exceptionCons = dependencyChecker.linkMethod(new MethodReference( NoClassDefFoundError.class, "", String.class, void.class), null); exceptionCons.use(); @@ -422,6 +426,29 @@ public class TeaVM implements TeaVMHost, ServiceRepository { listener.begin(renderer, target); } sourceWriter.append("\"use strict\";").newLine(); + + + // Keep track of current running thread by overriding setTimeout + sourceWriter.append("self.old_setTimeout=self.setTimeout;").softNewLine(); + sourceWriter.append("self.setTimeout=function(f,interval){").indent().softNewLine(); + MethodReference currentThreadRef = new MethodReference( + Thread.class, "currentThread", Thread.class); + MethodReference setCurrentThreadRef = new MethodReference( + Thread.class, "setCurrentThread", Thread.class, void.class); + MethodReference getMainThreadRef = new MethodReference(Thread.class, "getMainThread", Thread.class); + + sourceWriter.append("var currThread = ").appendMethodBody(currentThreadRef).append("();").softNewLine(); + sourceWriter.append("var callback = function(){").indent().softNewLine(); + sourceWriter.appendMethodBody(setCurrentThreadRef).append("(currThread);").softNewLine(); + sourceWriter.append("try{f();} finally {").softNewLine(); + sourceWriter.appendMethodBody(setCurrentThreadRef).append("("). + appendMethodBody(getMainThreadRef).append("());}").softNewLine(); + sourceWriter.outdent().append("};").softNewLine(); + sourceWriter.append("self.old_setTimeout(callback, interval);").softNewLine(); + sourceWriter.outdent().append("};").softNewLine(); + + // END Thread stuff + renderer.renderRuntime(); for (ClassNode clsNode : clsNodes) { ClassReader cls = classSet.get(clsNode.getName()); diff --git a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java index 27eda6f2f..3b16394a1 100644 --- a/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java +++ b/teavm-samples/teavm-samples-async/src/main/java/org/teavm/samples/async/AsyncProgram.java @@ -46,9 +46,9 @@ public final class AsyncProgram { } } - }); + }, "Test Thread"); t.start(); - + System.out.println("Should be main -> Current thread is "+Thread.currentThread().getName()); System.out.println("Now trying wait..."); lock.wait(20000); @@ -57,12 +57,16 @@ public final class AsyncProgram { } private static void doRun(Object lock) throws InterruptedException { + System.out.println("Current thread is "+Thread.currentThread().getName()); System.out.println("Executing timer task"); Thread.sleep(2000); + System.out.println("Current thread is "+Thread.currentThread().getName()); System.out.println("Calling lock.notify()"); lock.notify(); + System.out.println("Current thread is "+Thread.currentThread().getName()); System.out.println("Finished calling lock.notify()"); Thread.sleep(5000); + System.out.println("Current thread is "+Thread.currentThread().getName()); System.out.println("Finished another 5 second sleep"); }