From 72cb3973d6f1781c84940d75a56c10b316629867 Mon Sep 17 00:00:00 2001 From: Steve Hannah Date: Mon, 2 Feb 2015 12:45:32 -0800 Subject: [PATCH] Added some implementations for Object.wait(), Object.notify(), Object.notifyAll(), and Thread.start() to try to emulate the behaviour of multithreaded environments. --- .../java/lang/ObjectNativeGenerator.java | 75 +++++++++++++++++++ .../org/teavm/classlib/java/lang/TObject.java | 35 ++++++--- .../org/teavm/classlib/java/lang/TThread.java | 6 ++ .../java/lang/ThreadNativeGenerator.java | 29 ++++++- .../org/teavm/samples/async/AsyncProgram.java | 40 ++++++++++ 5 files changed, 173 insertions(+), 12 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index 3176e3319..77212fa66 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -43,6 +43,15 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu case "clone": generateClone(context, writer); break; + case "wait": + generateWait(context, writer); + break; + case "notify": + generateNotify(context, writer); + break; + case "notifyAll": + generateNotifyAll(context, writer); + break; } } @@ -70,6 +79,10 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu case "wrap": method.getVariable(1).connect(method.getResult()); break; + //case "wait": + // method.getVariable(0).connect(method.getResult()); + // break; + } } @@ -107,4 +120,66 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu private void generateWrap(InjectorContext context) throws IOException { context.writeExpr(context.getArgument(0)); } + + private void generateWait(GeneratorContext context, SourceWriter writer) throws IOException { + String pname = context.getParameterName(1); + String obj = context.getParameterName(0); + writer.append("(function(){").indent().softNewLine(); + writer.append("var completed = false;").softNewLine(); + writer.append("var retCallback = ").append(context.getCompleteContinuation()).append(";").softNewLine(); + writer.append("console.log(retCallback);").softNewLine(); + writer.append("var callback = function(){").indent().softNewLine(); + writer.append("if (completed){return;} completed=true;").softNewLine(); + writer.append("retCallback();").softNewLine(); + writer.outdent().append("};").softNewLine(); + writer.append("if (").append(pname).append(">0){").indent().softNewLine(); + writer.append("setTimeout(callback, ").append(pname).append(");").softNewLine(); + writer.outdent().append("}").softNewLine(); + addNotifyListener(context, writer, "callback"); + writer.outdent().append("})();").softNewLine(); + + + + } + + private void generateNotify(GeneratorContext context, SourceWriter writer) throws IOException { + sendNotify(context, writer); + } + + private void generateNotifyAll(GeneratorContext context, SourceWriter writer) throws IOException { + sendNotifyAll(context, writer); + } + + private String getNotifyListeners(GeneratorContext context){ + return context.getParameterName(0)+".__notifyListeners"; + } + + private void addNotifyListener(GeneratorContext context, SourceWriter writer, String callback) throws IOException { + String lArr = getNotifyListeners(context); + writer.append(lArr).append("=").append(lArr).append("||[];").softNewLine(); + writer.append(lArr).append(".push(").append(callback).append(");").softNewLine(); + } + + private void sendNotify(GeneratorContext context, SourceWriter writer) throws IOException { + String lArr = getNotifyListeners(context); + writer.append("setTimeout(function(){").indent().softNewLine(); + writer.append("if (!").append(lArr).append(" || ").append(lArr).append(".length===0){return;}").softNewLine(); + writer.append("var m = ").append(lArr).append(".shift();").softNewLine(); + writer.append("console.log('Notify callback : '+m);").softNewLine(); + writer.append("m.apply(null);").softNewLine(); + writer.outdent().append("}, 0);").softNewLine(); + } + + private void sendNotifyAll(GeneratorContext context, SourceWriter writer) throws IOException { + String obj = context.getParameterName(0); + String lArr = getNotifyListeners(context); + writer.append("setTimeout(function(){").indent().softNewLine(); + writer.append("if (!").append(lArr).append("){return;}").softNewLine(); + writer.append("while (").append(lArr).append(".length>0){").indent().softNewLine(); + writer.append(lArr).append(".shift().call(null);").softNewLine(); + writer.outdent().append("}"); + writer.outdent().append("}, 0);").softNewLine(); + + } + } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 6759d3cc7..5de13b11f 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -15,11 +15,13 @@ */ package org.teavm.classlib.java.lang; + import org.teavm.dependency.PluggableDependency; import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.InjectedBy; import org.teavm.javascript.ni.Rename; import org.teavm.javascript.ni.Superclass; +import org.teavm.runtime.Async; /** * @@ -62,26 +64,37 @@ public class TObject { @Override protected native Object clone() throws TCloneNotSupportedException; + @GeneratedBy(ObjectNativeGenerator.class) @Rename("notify") - public final void notify0() { - } + public native final void notify0(); + + @GeneratedBy(ObjectNativeGenerator.class) @Rename("notifyAll") - public final void notifyAll0() { - } - - @SuppressWarnings("unused") + public native final void notifyAll0(); + + @Rename("wait") - public final void wait0(long timeout) throws TInterruptedException { + public final void wait0(long timeout) throws TInterruptedException{ + try { + wait(timeout, 0); + } catch ( InterruptedException ex){ + throw new TInterruptedException(); + } } - - @SuppressWarnings("unused") + + @Async + @GeneratedBy(ObjectNativeGenerator.class) @Rename("wait") - public final void wait0(long timeout, int nanos) throws TInterruptedException { - } + public native final void wait0(long timeout, int nanos) throws TInterruptedException; @Rename("wait") public final void wait0() throws TInterruptedException { + try { + wait(0l); + } catch (InterruptedException ex) { + throw new TInterruptedException(); + } } @Override 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 12ff437f9..926c03d5a 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 @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.dependency.PluggableDependency; import org.teavm.javascript.ni.GeneratedBy; import org.teavm.runtime.Async; @@ -44,6 +45,11 @@ public class TThread extends TObject implements TRunnable { this.target = target; } + @PluggableDependency(ThreadNativeGenerator.class) + @GeneratedBy(ThreadNativeGenerator.class) + public native void start(); + + @Override public void run() { if (target != null) { 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 2560d7931..ff1ab7d8b 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 @@ -17,24 +17,45 @@ package org.teavm.classlib.java.lang; import java.io.IOException; import org.teavm.codegen.SourceWriter; +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; /** * * @author Alexey Andreev */ -public class ThreadNativeGenerator implements Generator { +public class ThreadNativeGenerator implements Generator, DependencyPlugin { + + private static final MethodReference runRef = new MethodReference(Thread.class, + "run", void.class); + @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { if (methodRef.getName().equals("sleep")) { generateSleep(context, writer); } else if (methodRef.getName().equals("yield")) { generateYield(context, writer); + } else if ( methodRef.getName().equals("start")){ + generateStart(context, writer); } } + + + public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { + switch (method.getReference().getName()) { + case "start": { + MethodDependency performMethod = agent.linkMethod(runRef, null); + performMethod.use(); + break; + } + } + } private void generateSleep(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("setTimeout(function() {").indent().softNewLine(); writer.append(context.getCompleteContinuation()).append("();").softNewLine(); @@ -46,4 +67,10 @@ public class ThreadNativeGenerator implements Generator { writer.append(context.getCompleteContinuation()).append("();").softNewLine(); writer.outdent().append("},").ws().append("0);").softNewLine(); } + + private void generateStart(GeneratorContext context, SourceWriter writer) throws IOException { + String obj = context.getParameterName(0); + + writer.append("setTimeout(function() {").append(obj).append(".").appendMethod(runRef).append("();},0);").softNewLine(); + } } 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 fca37cc4a..27eda6f2f 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 @@ -15,6 +15,7 @@ */ package org.teavm.samples.async; + /** * * @author Alexey Andreev @@ -27,8 +28,44 @@ public final class AsyncProgram { withoutAsync(); System.out.println(); withAsync(); + + System.out.println(); + + + + final Object lock = new Object(); + + Thread t = new Thread(new Runnable(){ + + @Override + public void run() { + try { + doRun(lock); + } catch (InterruptedException ex){ + System.out.println(ex.getMessage()); + } + } + + }); + t.start(); + + System.out.println("Now trying wait..."); + + lock.wait(20000); + System.out.println("Finished waiting"); + } + private static void doRun(Object lock) throws InterruptedException { + System.out.println("Executing timer task"); + Thread.sleep(2000); + System.out.println("Calling lock.notify()"); + lock.notify(); + System.out.println("Finished calling lock.notify()"); + Thread.sleep(5000); + System.out.println("Finished another 5 second sleep"); + } + private static void withoutAsync() { System.out.println("Start sync"); for (int i = 0; i < 20; ++i) { @@ -54,6 +91,9 @@ public final class AsyncProgram { Thread.sleep(1000); } } + System.out.println("2nd Thread.sleep in same method"); + Thread.sleep(1000); + System.out.println("Complete async"); } }