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":
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();
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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 <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
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();
}
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.samples.async;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
@ -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");
}
}