Fix monitorenter

This commit is contained in:
konsoletyper 2015-02-14 00:24:24 +04:00
parent f35f06097c
commit f93b35ce80
11 changed files with 349 additions and 147 deletions

View File

@ -15,6 +15,7 @@
*/
package org.teavm.classlib.java.lang;
import org.teavm.dom.browser.TimerHandler;
import org.teavm.dom.browser.Window;
import org.teavm.javascript.spi.Async;
import org.teavm.javascript.spi.Rename;
@ -32,40 +33,50 @@ import org.teavm.platform.async.AsyncCallback;
@Superclass("")
public class TObject {
private static final Window window = (Window)JS.getGlobal();
private TThread owner;
private TObject monitorLock;
private int monitorCount;
private JSArray<NotifyListener> notifyListeners;
private Lock lock;
interface NotifyListener extends JSObject {
void handleNotify();
private static class Lock {
TThread owner;
int count;
public Lock() {
this.owner = TThread.currentThread();
count = 1;
}
}
static void monitorEnter(TObject o){
if (o.monitorLock == null ){
o.monitorLock = new TObject();
}
while (o.owner != null && o.owner != TThread.currentThread()) {
try {
o.monitorLock.wait();
} catch (InterruptedException ex) {
interface NotifyListener extends JSObject {
boolean handleNotify();
}
}
static void monitorEnter(TObject o) {
if (o.lock == null) {
o.lock = new Lock();
return;
}
if (o.lock.owner != TThread.currentThread()) {
while (o.lock != null) {
try {
o.lock.wait();
} catch (InterruptedException ex) {
}
}
o.lock = new Lock();
} else {
o.lock.count++;
}
o.owner = TThread.currentThread();
o.monitorCount++;
}
static void monitorExit(TObject o){
o.monitorCount--;
if (o.monitorCount == 0 && o.monitorLock != null) {
o.owner = null;
o.monitorLock.notifyAll();
if (o.lock != null && o.lock.count-- == 0) {
o.lock.notifyAll();
o.lock = null;
}
}
static boolean holdsLock(TObject o){
return o.owner == TThread.currentThread();
return o.lock != null && o.lock.owner == TThread.currentThread();
}
@Rename("fakeInit")
@ -114,12 +125,16 @@ public class TObject {
@Rename("notify")
public final void notify0(){
if (notifyListeners != null && notifyListeners.getLength() > 0){
notifyListeners.shift().handleNotify();
if (notifyListeners != null) {
while (notifyListeners.getLength() > 0 && notifyListeners.shift().handleNotify()) {
// repeat loop
}
if (notifyListeners.getLength() == 0) {
notifyListeners = null;
}
}
}
@Rename("notifyAll")
public final void notifyAll0(){
if (notifyListeners != null){
@ -127,6 +142,7 @@ public class TObject {
while (notifyListeners.getLength() > 0) {
listeners.push(notifyListeners.shift());
}
notifyListeners = null;
while (listeners.getLength() > 0) {
listeners.shift().handleNotify();
}
@ -144,7 +160,7 @@ public class TObject {
@Async
@Rename("wait")
public native final void wait0(long timeout, int nanos) throws TInterruptedException;
private native final void wait0(long timeout, int nanos) throws TInterruptedException;
@Rename("wait")
public final void wait0(long timeout, int nanos, final AsyncCallback<Void> callback) {
@ -152,24 +168,55 @@ public class TObject {
notifyListeners = window.newArray();
}
final TThread currentThread = TThread.currentThread();
notifyListeners.push(new NotifyListener() {
@Override
public void handleNotify() {
TThread.setCurrentThread(currentThread);
try {
callback.complete(null);
} finally {
TThread.setCurrentThread(TThread.getMainThread());
}
final NotifyListenerImpl listener = new NotifyListenerImpl(callback, currentThread);
notifyListeners.push(listener);
if (timeout == 0 && nanos == 0) {
return;
}
listener.timerId = window.setTimeout(listener, timeout);
}
private static class NotifyListenerImpl implements NotifyListener, TimerHandler {
final AsyncCallback<Void> callback;
final TThread currentThread;
int timerId = -1;
boolean finished;
public NotifyListenerImpl(AsyncCallback<Void> callback, TThread currentThread) {
this.callback = callback;
this.currentThread = currentThread;
}
@Override
public boolean handleNotify() {
if (finished) {
return false;
}
});
TThread.setCurrentThread(currentThread);
if (timerId >= 0) {
window.clearTimeout(timerId);
timerId = -1;
}
finished = true;
try {
callback.complete(null);
} finally {
TThread.setCurrentThread(TThread.getMainThread());
}
return true;
}
@Override
public void onTimer() {
handleNotify();
}
}
@Rename("wait")
public final void wait0() throws TInterruptedException {
try {
wait(0l);
} catch ( InterruptedException ex){
} catch (InterruptedException ex) {
throw new TInterruptedException();
}
}

View File

@ -73,31 +73,29 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
public void visit(MonitorEnterStatement statement) {
if (async){
try {
MethodReference monitorEnterRef = new MethodReference(
Object.class, "monitorEnter", Object.class, void.class);
writer.appendMethodBody(monitorEnterRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
try {
MethodReference monitorEnterRef = new MethodReference(
Object.class, "monitorEnter", Object.class, void.class);
writer.appendMethodBody(monitorEnterRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(",").ws();
writer.append("$rt_continue($part_").append(statement.getAsyncTarget()).append(')');
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
}
@Override
public void visit(MonitorExitStatement statement) {
if (async){
try {
MethodReference monitorExitRef = new MethodReference(
Object.class, "monitorExit", Object.class, void.class);
writer.appendMethodBody(monitorExitRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
try {
MethodReference monitorExitRef = new MethodReference(
Object.class, "monitorExit", Object.class, void.class);
writer.appendMethodBody(monitorExitRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
}
@ -938,7 +936,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
StringBuilder sb = new StringBuilder();
int index = blockIdMap.size();
do {
sb.append(variablePartNames.charAt(index % variableNames.length()));
sb.append(variablePartNames.charAt(index % variablePartNames.length()));
index /= variablePartNames.length();
} while (index > 0);
name = "$b" + sb;

View File

@ -669,11 +669,10 @@ class StatementGenerator implements InstructionVisitor {
@Override
public void visit(MonitorEnterInstruction insn) {
MonitorEnterStatement stmt = new MonitorEnterStatement();
stmt.setLocation(currentLocation);
stmt.setObjectRef(Expr.var(insn.getObjectRef().getIndex()));
stmt.setAsyncTarget(asyncTarget);
statements.add(stmt);
}
@ -681,7 +680,6 @@ class StatementGenerator implements InstructionVisitor {
public void visit(MonitorExitInstruction insn) {
MonitorExitStatement stmt = new MonitorExitStatement();
stmt.setLocation(currentLocation);
stmt.setObjectRef(Expr.var(insn.getObjectRef().getIndex()));
statements.add(stmt);
}

View File

@ -20,41 +20,36 @@ package org.teavm.javascript.ast;
* @author shannah
*/
public class MonitorEnterStatement extends Statement {
private NodeLocation location;
private Expr objectRef;
private Integer asyncTarget;
@Override
public void acceptVisitor(StatementVisitor visitor) {
visitor.visit(this);
}
/**
* @return the location
*/
public NodeLocation getLocation() {
return location;
}
/**
* @param location the location to set
*/
public void setLocation(NodeLocation location) {
this.location = location;
}
/**
* @return the objectRef
*/
public Expr getObjectRef() {
return objectRef;
}
/**
* @param objectRef the objectRef to set
*/
public void setObjectRef(Expr objectRef) {
this.objectRef = objectRef;
}
public Integer getAsyncTarget() {
return asyncTarget;
}
public void setAsyncTarget(Integer asyncTarget) {
this.asyncTarget = asyncTarget;
}
}

View File

@ -20,41 +20,28 @@ package org.teavm.javascript.ast;
* @author shannah
*/
public class MonitorExitStatement extends Statement {
private NodeLocation location;
private Expr objectRef;
private Integer asyncTarget;
@Override
public void acceptVisitor(StatementVisitor visitor) {
visitor.visit(this);
}
/**
* @return the location
*/
public NodeLocation getLocation() {
return location;
}
/**
* @param location the location to set
*/
public void setLocation(NodeLocation location) {
this.location = location;
}
/**
* @return the objectRef
*/
public Expr getObjectRef() {
return objectRef;
}
/**
* @param objectRef the objectRef to set
*/
public void setObjectRef(Expr objectRef) {
this.objectRef = objectRef;
}
}

View File

@ -24,6 +24,7 @@ import org.teavm.javascript.spi.Async;
import org.teavm.javascript.spi.InjectedBy;
import org.teavm.javascript.spi.Sync;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
/**
*
@ -61,6 +62,17 @@ public class AsyncMethodFinder {
}
if (method.getAnnotations().get(Async.class.getName()) != null) {
add(method.getReference());
} else if (method.getProgram() != null) {
ProgramReader program = method.getProgram();
AsyncInstructionFinder insnFinder = new AsyncInstructionFinder();
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i);
block.readAllInstructions(insnFinder);
if (insnFinder.hasAsync) {
add(method.getReference());
break;
}
}
}
}
}
@ -174,4 +186,166 @@ public class AsyncMethodFinder {
}
}
}
private class AsyncInstructionFinder implements InstructionReader {
boolean hasAsync;
@Override
public void location(InstructionLocation location) {
}
@Override
public void nop() {
}
@Override
public void classConstant(VariableReader receiver, ValueType cst) {
}
@Override
public void nullConstant(VariableReader receiver) {
}
@Override
public void integerConstant(VariableReader receiver, int cst) {
}
@Override
public void longConstant(VariableReader receiver, long cst) {
}
@Override
public void floatConstant(VariableReader receiver, float cst) {
}
@Override
public void doubleConstant(VariableReader receiver, double cst) {
}
@Override
public void stringConstant(VariableReader receiver, String cst) {
}
@Override
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) {
}
@Override
public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
}
@Override
public void assign(VariableReader receiver, VariableReader assignee) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
NumericOperandType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
CastIntegerDirection targetType) {
}
@Override
public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
BasicBlockReader alternative) {
}
@Override
public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
BasicBlockReader consequent, BasicBlockReader alternative) {
}
@Override
public void jump(BasicBlockReader target) {
}
@Override
public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
BasicBlockReader defaultTarget) {
}
@Override
public void exit(VariableReader valueToReturn) {
}
@Override
public void raise(VariableReader exception) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType,
List<? extends VariableReader> dimensions) {
}
@Override
public void create(VariableReader receiver, String type) {
}
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
ValueType fieldType) {
}
@Override
public void putField(VariableReader instance, FieldReference field, VariableReader value) {
}
@Override
public void arrayLength(VariableReader receiver, VariableReader array) {
}
@Override
public void cloneArray(VariableReader receiver, VariableReader array) {
}
@Override
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
}
@Override
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
}
@Override
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
}
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
}
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
}
@Override
public void initClass(String className) {
}
@Override
public void nullCheck(VariableReader receiver, VariableReader value) {
}
@Override
public void monitorEnter(VariableReader objectRef) {
hasAsync = true;
}
@Override
public void monitorExit(VariableReader objectRef) {
}
}
}

View File

@ -19,6 +19,7 @@ import java.util.*;
import org.teavm.model.*;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction;
/**
*
@ -58,61 +59,67 @@ public class AsyncProgramSplitter {
int last = 0;
for (int i = 0; i < sourceBlock.getInstructions().size(); ++i) {
Instruction insn = sourceBlock.getInstructions().get(i);
Integer receiver;
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction)insn;
if (!asyncMethods.contains(invoke.getMethod())) {
continue;
}
// If we met asynchronous invocation...
// Copy portion of current block from last occurence (or from start) to i'th instruction.
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock,
last, i + 1, targetBlock.getProgram()));
targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock,
targetBlock.getProgram()));
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
if (tryCatch.getHandler() != null) {
Step next = new Step();
next.source = tryCatch.getHandler().getIndex();
next.targetPart = step.targetPart;
queue.add(next);
}
}
last = i + 1;
// If this instruction already separates program, end with current block and refer to the
// existing part
long key = ((long)step.source << 32) | i;
if (partMap.containsKey(key)) {
step.targetPart.blockSuccessors[targetBlock.getIndex()] = partMap.get(key);
continue taskLoop;
}
// Create a new part
Program nextProgram = createStubCopy(program);
Part part = new Part();
part.input = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null;
part.program = nextProgram;
int partId = parts.size();
parts.add(part);
part.blockSuccessors = new int[program.basicBlockCount() + 1];
Arrays.fill(part.blockSuccessors, -1);
// Mark current instruction as a separator and remember which part is in charge.
partMap.put(key, partId);
step.targetPart.blockSuccessors[targetBlock.getIndex()] = partId;
// Continue with a new block in the new part
targetBlock = nextProgram.createBasicBlock();
if (step.source > 0) {
JumpInstruction jumpToNextBlock = new JumpInstruction();
jumpToNextBlock.setTarget(targetBlock);
nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock);
nextProgram.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock,
nextProgram));
}
step.targetPart = part;
receiver = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null;
} else if (insn instanceof MonitorEnterInstruction) {
receiver = null;
} else {
continue;
}
// If we met asynchronous invocation...
// Copy portion of current block from last occurrence (or from start) to i'th instruction.
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock,
last, i + 1, targetBlock.getProgram()));
targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock,
targetBlock.getProgram()));
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
if (tryCatch.getHandler() != null) {
Step next = new Step();
next.source = tryCatch.getHandler().getIndex();
next.targetPart = step.targetPart;
queue.add(next);
}
}
last = i + 1;
// If this instruction already separates program, end with current block and refer to the
// existing part
long key = ((long)step.source << 32) | i;
if (partMap.containsKey(key)) {
step.targetPart.blockSuccessors[targetBlock.getIndex()] = partMap.get(key);
continue taskLoop;
}
// Create a new part
Program nextProgram = createStubCopy(program);
Part part = new Part();
part.input = receiver;
part.program = nextProgram;
int partId = parts.size();
parts.add(part);
part.blockSuccessors = new int[program.basicBlockCount() + 1];
Arrays.fill(part.blockSuccessors, -1);
// Mark current instruction as a separator and remember which part is in charge.
partMap.put(key, partId);
step.targetPart.blockSuccessors[targetBlock.getIndex()] = partId;
// Continue with a new block in the new part
targetBlock = nextProgram.createBasicBlock();
if (step.source > 0) {
JumpInstruction jumpToNextBlock = new JumpInstruction();
jumpToNextBlock.setTarget(targetBlock);
nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock);
nextProgram.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock,
nextProgram));
}
step.targetPart = part;
}
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock,
last, sourceBlock.getInstructions().size(), targetBlock.getProgram()));

View File

@ -313,12 +313,10 @@ public class MissingItemsProcessor {
@Override
public void visit(MonitorEnterInstruction insn) {
}
@Override
public void visit(MonitorExitInstruction insn) {
}
};
}

View File

@ -214,6 +214,6 @@ public class UsageExtractor implements InstructionVisitor {
@Override
public void visit(MonitorExitInstruction insn) {
usedVariables = new Variable[] {insn.getObjectRef()};
usedVariables = new Variable[] {insn.getObjectRef() };
}
}

View File

@ -382,12 +382,12 @@ public class LoopInvariantMotion implements MethodOptimization {
@Override
public void visit(MonitorEnterInstruction insn) {
}
@Override
public void visit(MonitorExitInstruction insn) {
}
}
@ -574,12 +574,10 @@ public class LoopInvariantMotion implements MethodOptimization {
@Override
public void visit(MonitorEnterInstruction insn) {
}
@Override
public void visit(MonitorExitInstruction insn) {
}
}
}

View File

@ -207,12 +207,12 @@ public final class VariableEscapeAnalyzer {
@Override
public void visit(MonitorEnterInstruction insn) {
escaping[insn.getObjectRef().getIndex()] = true;
}
@Override
public void visit(MonitorExitInstruction insn) {
escaping[insn.getObjectRef().getIndex()] = true;
}
}
}