Added some implementations for Object.wait(), Object.notify(), Object.notifyAll(), and Thread.start() to try to emulate the behaviour of multithreaded environments.

This commit is contained in:
Steve Hannah 2015-02-02 12:45:32 -08:00
parent 7c084effb0
commit 72cb3973d6
5 changed files with 173 additions and 12 deletions

View File

@ -43,6 +43,15 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
case "clone": case "clone":
generateClone(context, writer); generateClone(context, writer);
break; 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": case "wrap":
method.getVariable(1).connect(method.getResult()); method.getVariable(1).connect(method.getResult());
break; 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 { private void generateWrap(InjectorContext context) throws IOException {
context.writeExpr(context.getArgument(0)); 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();
}
} }

View File

@ -15,11 +15,13 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.GeneratedBy;
import org.teavm.javascript.ni.InjectedBy; import org.teavm.javascript.ni.InjectedBy;
import org.teavm.javascript.ni.Rename; import org.teavm.javascript.ni.Rename;
import org.teavm.javascript.ni.Superclass; import org.teavm.javascript.ni.Superclass;
import org.teavm.runtime.Async;
/** /**
* *
@ -62,26 +64,37 @@ public class TObject {
@Override @Override
protected native Object clone() throws TCloneNotSupportedException; protected native Object clone() throws TCloneNotSupportedException;
@GeneratedBy(ObjectNativeGenerator.class)
@Rename("notify") @Rename("notify")
public final void notify0() { public native final void notify0();
}
@GeneratedBy(ObjectNativeGenerator.class)
@Rename("notifyAll") @Rename("notifyAll")
public final void notifyAll0() { public native final void notifyAll0();
}
@SuppressWarnings("unused")
@Rename("wait") @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") @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") @Rename("wait")
public final void wait0() throws TInterruptedException { public final void wait0() throws TInterruptedException {
try {
wait(0l);
} catch (InterruptedException ex) {
throw new TInterruptedException();
}
} }
@Override @Override

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.GeneratedBy;
import org.teavm.runtime.Async; import org.teavm.runtime.Async;
@ -44,6 +45,11 @@ public class TThread extends TObject implements TRunnable {
this.target = target; this.target = target;
} }
@PluggableDependency(ThreadNativeGenerator.class)
@GeneratedBy(ThreadNativeGenerator.class)
public native void start();
@Override @Override
public void run() { public void run() {
if (target != null) { if (target != null) {

View File

@ -17,24 +17,45 @@ package org.teavm.classlib.java.lang;
import java.io.IOException; import java.io.IOException;
import org.teavm.codegen.SourceWriter; 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.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
* *
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public class ThreadNativeGenerator implements Generator { public class ThreadNativeGenerator implements Generator, DependencyPlugin {
private static final MethodReference runRef = new MethodReference(Thread.class,
"run", void.class);
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
if (methodRef.getName().equals("sleep")) { if (methodRef.getName().equals("sleep")) {
generateSleep(context, writer); generateSleep(context, writer);
} else if (methodRef.getName().equals("yield")) { } else if (methodRef.getName().equals("yield")) {
generateYield(context, writer); 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 { private void generateSleep(GeneratorContext context, SourceWriter writer) throws IOException {
writer.append("setTimeout(function() {").indent().softNewLine(); writer.append("setTimeout(function() {").indent().softNewLine();
writer.append(context.getCompleteContinuation()).append("();").softNewLine(); writer.append(context.getCompleteContinuation()).append("();").softNewLine();
@ -46,4 +67,10 @@ public class ThreadNativeGenerator implements Generator {
writer.append(context.getCompleteContinuation()).append("();").softNewLine(); writer.append(context.getCompleteContinuation()).append("();").softNewLine();
writer.outdent().append("},").ws().append("0);").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();
}
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.samples.async; package org.teavm.samples.async;
/** /**
* *
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
@ -27,8 +28,44 @@ public final class AsyncProgram {
withoutAsync(); withoutAsync();
System.out.println(); System.out.println();
withAsync(); 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() { private static void withoutAsync() {
System.out.println("Start sync"); System.out.println("Start sync");
for (int i = 0; i < 20; ++i) { for (int i = 0; i < 20; ++i) {
@ -54,6 +91,9 @@ public final class AsyncProgram {
Thread.sleep(1000); Thread.sleep(1000);
} }
} }
System.out.println("2nd Thread.sleep in same method");
Thread.sleep(1000);
System.out.println("Complete async"); System.out.println("Complete async");
} }
} }