Do away with the PromiseQueue.
Instead of queuing messages with promises in them until the promises are ready, send immediately. This is done by sending a port down which the receiver sends a port which can be used to signal completion of the outgoing promise. Review URL: https://chromereviews.googleplex.com/3573013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@427 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
+118
-19
@@ -317,6 +317,7 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
final boolean isVoid = isVoid(returnTypeNode);
|
||||
final boolean isSimple = isSimpleType(returnTypeNode);
|
||||
final boolean isProxy = isProxyType(returnTypeNode);
|
||||
final boolean isPromise = isPromise(returnTypeNode);
|
||||
if (!isVoid) {
|
||||
p("return ");
|
||||
if (!isSimple) {
|
||||
@@ -328,6 +329,16 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
p("Impl(");
|
||||
}
|
||||
}
|
||||
if (isProxy) {
|
||||
// Note that Promises are Proxies.
|
||||
p("new PromiseProxy");
|
||||
if (isPromise) {
|
||||
printTypeArguments(returnTypeNode);
|
||||
} else {
|
||||
p("<SendPort>");
|
||||
}
|
||||
p("(");
|
||||
}
|
||||
p("this.");
|
||||
if (isVoid) {
|
||||
p("send");
|
||||
@@ -353,6 +364,9 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
};
|
||||
params.acceptList(func.getParams());
|
||||
p("])");
|
||||
if (isProxy) {
|
||||
p(")");
|
||||
}
|
||||
if (!isSimple) {
|
||||
p(")");
|
||||
}
|
||||
@@ -396,8 +410,23 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
printFunctionName(member);
|
||||
p("\") {");
|
||||
nl();
|
||||
unpackParams(member);
|
||||
callTarget((DartMethodDefinition)member);
|
||||
int proxies = unpackParams(member);
|
||||
String extra = "";
|
||||
if (proxies != 0) {
|
||||
p(" Promise done = new Promise();");
|
||||
nl();
|
||||
p(" done.waitFor(promises, " + proxies + ");");
|
||||
nl();
|
||||
p(" done.addCompleteHandler((_) {");
|
||||
nl();
|
||||
gatherProxies(member);
|
||||
extra = " ";
|
||||
}
|
||||
callTarget((DartMethodDefinition)member, extra);
|
||||
if (proxies != 0) {
|
||||
p(" });");
|
||||
nl();
|
||||
}
|
||||
p(" }");
|
||||
first = false;
|
||||
}
|
||||
@@ -411,11 +440,11 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
nl();
|
||||
}
|
||||
|
||||
private void callTarget(DartMethodDefinition member) {
|
||||
private void callTarget(DartMethodDefinition member, String extra) {
|
||||
if (isConstructor(member)) {
|
||||
return;
|
||||
}
|
||||
p(" ");
|
||||
p(extra + " ");
|
||||
boolean isVoid = isVoid(member.getFunction().getReturnTypeNode());
|
||||
if (!isVoid) {
|
||||
printReturnType(member);
|
||||
@@ -431,14 +460,14 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
nl();
|
||||
String returnType = stringReturnType(member);
|
||||
if (stubInterfaces.contains(returnType)) {
|
||||
p(" SendPort port = Dispatcher.serve(new " + returnType + "$Dispatcher(");
|
||||
p(extra + " SendPort port = Dispatcher.serve(new " + returnType + "$Dispatcher(");
|
||||
printFunctionName(member);
|
||||
p("));");
|
||||
nl();
|
||||
p(" reply(port);");
|
||||
p(extra + " reply(port);");
|
||||
nl();
|
||||
} else if (!isVoid) {
|
||||
p(" reply(");
|
||||
p(extra + " reply(");
|
||||
printFunctionName(member);
|
||||
p(");");
|
||||
nl();
|
||||
@@ -521,9 +550,10 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
return strType.toString();
|
||||
}
|
||||
|
||||
private void unpackParams(DartNode member) {
|
||||
DartVisitor visitor = new DartVisitor() {
|
||||
private int unpackParams(DartNode member) {
|
||||
class UnpackVisitor extends DartVisitor {
|
||||
private int pos;
|
||||
int proxies;
|
||||
|
||||
@Override
|
||||
public boolean visit(DartTypeNode x, DartContext ctx) {
|
||||
@@ -541,6 +571,7 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
@Override
|
||||
public boolean visit(DartMethodDefinition x, DartContext ctx) {
|
||||
pos = 1;
|
||||
proxies = 0;
|
||||
for (DartParameter param : x.getFunction().getParams()) {
|
||||
p(" ");
|
||||
accept(param);
|
||||
@@ -554,23 +585,85 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
public boolean visit(DartParameter x, DartContext ctx) {
|
||||
boolean isSimpleType = isSimpleType(x.getTypeNode());
|
||||
|
||||
accept(x.getTypeNode());
|
||||
p(" ");
|
||||
accept(x.getName());
|
||||
p(" = ");
|
||||
if (!isSimpleType) {
|
||||
p("new ");
|
||||
if (isSimpleType) {
|
||||
accept(x.getTypeNode());
|
||||
p("Impl(new Promise<SendPort>.fromValue(");
|
||||
p(" ");
|
||||
accept(x.getName());
|
||||
p(" = ");
|
||||
} else {
|
||||
if (proxies == 0) {
|
||||
p("List<Promise<SendPort>> promises = new List<Promise<SendPort>>();");
|
||||
nl();
|
||||
p(" ");
|
||||
}
|
||||
p("promises.add(new PromiseProxy<SendPort>(new Promise<SendPort>.fromValue(");
|
||||
++proxies;
|
||||
//p("new ");
|
||||
//accept(x.getTypeNode());
|
||||
//p("Impl(new Promise<SendPort>.fromValue(");
|
||||
}
|
||||
p("message[" + pos + "]");
|
||||
if (!isSimpleType) {
|
||||
p("))");
|
||||
p(")))");
|
||||
}
|
||||
p(";");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
UnpackVisitor visitor = new UnpackVisitor();
|
||||
|
||||
visitor.accept(member);
|
||||
return visitor.proxies;
|
||||
}
|
||||
|
||||
private void gatherProxies(DartNode member) {
|
||||
class GatherVisitor extends DartVisitor {
|
||||
private int proxies;
|
||||
|
||||
@Override
|
||||
public boolean visit(DartTypeNode x, DartContext ctx) {
|
||||
accept(x.getIdentifier());
|
||||
printTypeArguments(x);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartIdentifier x, DartContext ctx) {
|
||||
p(x.getTargetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartMethodDefinition x, DartContext ctx) {
|
||||
proxies = 0;
|
||||
for (DartParameter param : x.getFunction().getParams()) {
|
||||
accept(param);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartParameter x, DartContext ctx) {
|
||||
boolean isSimpleType = isSimpleType(x.getTypeNode());
|
||||
|
||||
if (!isSimpleType) {
|
||||
p(" ");
|
||||
accept(x.getTypeNode());
|
||||
p(" ");
|
||||
accept(x.getName());
|
||||
p(" = new ");
|
||||
accept(x.getTypeNode());
|
||||
p("Impl(promises[" + proxies + "]);");
|
||||
++proxies;
|
||||
nl();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
GatherVisitor visitor = new GatherVisitor();
|
||||
|
||||
visitor.accept(member);
|
||||
}
|
||||
|
||||
@@ -608,8 +701,14 @@ public class DartIsolateStubGenerator extends AbstractBackend {
|
||||
* reply(port);
|
||||
* } else if (command == "deposit") {
|
||||
* int amount = message[1];
|
||||
* Proxy<Purse> source = new Proxy<Purse>.forPort(message[2]);
|
||||
* target.deposit(amount, source);
|
||||
* Promise<SendPort> port =
|
||||
* new PromiseProxy<SendPort>(new Promise<SendPort>.fromValue(message[2]));
|
||||
* port.addCompletionHandler((_) {
|
||||
* Purse$Proxy source = new Purse$ProxyImpl(port);
|
||||
* target.deposit(amount, source);
|
||||
* });
|
||||
* //Proxy<Purse> source = new Proxy<Purse>.forPort(message[2]);
|
||||
* //target.deposit(amount, source);
|
||||
* } else {
|
||||
* // TODO(kasperl,benl): Somehow throw an exception instead.
|
||||
* reply("Exception: command not understood.");
|
||||
|
||||
@@ -7,17 +7,8 @@ class SendPortImpl implements SendPort {
|
||||
const SendPortImpl(this._workerId, this._isolateId, this._receivePortId);
|
||||
|
||||
void send(var message, [SendPort replyTo = null]) {
|
||||
if (PromiseQueue.isEmpty()) {
|
||||
this._sendNow(message, replyTo);
|
||||
} else {
|
||||
_enqueueSend(message, replyTo);
|
||||
}
|
||||
}
|
||||
|
||||
void _enqueueSend(var message, SendPort replyTo) {
|
||||
PromiseQueue.enqueue(const []).then((ignored) {
|
||||
this._sendNow(message, replyTo);
|
||||
});
|
||||
// TODO(kasperl): get rid of _sendNow.
|
||||
this._sendNow(message, replyTo);
|
||||
}
|
||||
|
||||
void _sendNow(var message, SendPort replyTo) native;
|
||||
@@ -179,10 +170,6 @@ class IsolateNatives {
|
||||
|
||||
|
||||
class _IsolateJsUtil {
|
||||
static void _promiseQueueProcess() native {
|
||||
PromiseQueue.process();
|
||||
}
|
||||
|
||||
static void _startIsolate(Isolate isolate, SendPort replyTo) native {
|
||||
ReceivePort port = new ReceivePort();
|
||||
replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
|
||||
|
||||
@@ -66,7 +66,6 @@ function isolate$receiveMessage(port, isolate,
|
||||
var message = isolate$deserializeMessage(serializedMessage);
|
||||
var replyTo = isolate$deserializeMessage(serializedReplyTo);
|
||||
native_ReceivePortImpl__invokeCallback(port, message, replyTo);
|
||||
native__IsolateJsUtil__promiseQueueProcess();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -275,6 +275,19 @@ class ProxyImpl {
|
||||
_promise = port;
|
||||
}
|
||||
|
||||
// Note that comparing proxies or using them in maps is illegal
|
||||
// until they complete.
|
||||
bool operator ==(var other) {
|
||||
return (other is ProxyImpl) && _promise.value == other._promise.value;
|
||||
}
|
||||
|
||||
int hashCode() => _promise.value.hashCode();
|
||||
|
||||
// TODO: consider making this extend Promise<SendPort> instead?
|
||||
void addCompleteHandler(void completeHandler()) {
|
||||
_promise.addCompleteHandler((_) => completeHandler());
|
||||
}
|
||||
|
||||
static ReceivePort register(Dispatcher dispatcher) {
|
||||
if (_dispatchers === null) {
|
||||
_dispatchers = new Map<SendPort, Dispatcher>();
|
||||
@@ -289,7 +302,7 @@ class ProxyImpl {
|
||||
Dispatcher dispatcher = _dispatchers[_promise.value];
|
||||
if (dispatcher !== null) return dispatcher.target;
|
||||
}
|
||||
throw "Cannot access object of non-local proxy.";
|
||||
throw new Exception("Cannot access object of non-local proxy.");
|
||||
}
|
||||
|
||||
void send(List message) {
|
||||
@@ -315,39 +328,46 @@ class ProxyImpl {
|
||||
});
|
||||
}
|
||||
|
||||
bool operator ==(var other) {
|
||||
return this === other;
|
||||
}
|
||||
|
||||
// FIXME(benl): generate a more useful hashCode.
|
||||
int hashCode() => 0;
|
||||
|
||||
// Marshal the [message] and pass it to the [process] callback
|
||||
// function once this proxy and all proxies in the message are
|
||||
// resolved.
|
||||
Promise _marshal(List message, process(List marshalled)) {
|
||||
final promises = new List<Promise>();
|
||||
promises.add(_promise);
|
||||
// function. Any promises are converted to a port which expects to
|
||||
// receive a port from the other side down which the remote promise
|
||||
// can be completed by sending the promise's completion value.
|
||||
Promise _marshal(List message, process(List marshalled)) {
|
||||
return _promise.then((SendPort port) {
|
||||
List marshalled = new List(message.length);
|
||||
|
||||
var marshalled = new List(message.length);
|
||||
for (int i = 0; i < marshalled.length; i++) {
|
||||
var entry = message[i];
|
||||
marshalled[i] = entry; // TODO(kasperl): We probably have to copy here.
|
||||
if (entry is Proxy) {
|
||||
promises.add(entry._promise);
|
||||
} else if (entry is Promise) {
|
||||
promises.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return PromiseQueue.enqueue(promises).then((ignored) {
|
||||
for (int i = 0; i < marshalled.length; i++) {
|
||||
var entry = marshalled[i];
|
||||
var entry = message[i];
|
||||
if (entry is Proxy) {
|
||||
marshalled[i] = entry._promise.value;
|
||||
} else if (entry is Promise) {
|
||||
marshalled[i] = entry.value;
|
||||
entry = entry._promise;
|
||||
}
|
||||
// Obviously this will be true if [entry] was a Proxy.
|
||||
if (entry is Promise) {
|
||||
// Note that we could optimise this by just sending the value
|
||||
// if the promise is already complete. Let's get this working
|
||||
// first!
|
||||
|
||||
// This port will receive a SendPort that can be used to
|
||||
// signal completion of this promise to the corresponding
|
||||
// promise that the other end has created.
|
||||
ReceivePort receiveCompleter = new ReceivePort.singleShot();
|
||||
marshalled[i] = receiveCompleter.toSendPort();
|
||||
Promise<SendPort> completer = new Promise<SendPort>();
|
||||
receiveCompleter.receive((var msg, SendPort replyPort) {
|
||||
completer.complete(msg[0]);
|
||||
});
|
||||
entry.addCompleteHandler((value) {
|
||||
completer.addCompleteHandler((SendPort port) {
|
||||
port.send([value], null);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// FIXME(kasperl, benl): this should probably be a copy?
|
||||
marshalled[i] = entry;
|
||||
}
|
||||
if (marshalled[i] is ReceivePort) {
|
||||
throw new Exception("Despite the documentation, you cannot send a ReceivePort");
|
||||
}
|
||||
}
|
||||
return process(marshalled);
|
||||
}).flatten();
|
||||
@@ -357,60 +377,3 @@ class ProxyImpl {
|
||||
static Map<SendPort, Dispatcher> _dispatchers;
|
||||
|
||||
}
|
||||
|
||||
|
||||
class PromiseQueue {
|
||||
|
||||
// Enqueue an element that depends on a list of promises. The
|
||||
// returned promise is resolved when all the input promises have
|
||||
// been resolved.
|
||||
static Promise enqueue(List<Promise> dependencies) {
|
||||
if (_queue === null) {
|
||||
_queue = new Queue<Function>();
|
||||
}
|
||||
|
||||
// Keep track of how many unresolved promises we're waiting for.
|
||||
int unresolved = dependencies.length;
|
||||
void notifyResolved(ignored) {
|
||||
assert(unresolved > 0);
|
||||
unresolved--;
|
||||
}
|
||||
|
||||
// Register a callback on each of the dependencies.
|
||||
for (Promise promise in dependencies) {
|
||||
promise.then(notifyResolved);
|
||||
}
|
||||
|
||||
final Promise result = new Promise();
|
||||
_queue.addLast(() {
|
||||
if (unresolved > 0) return false;
|
||||
// TODO(kasperl): It seems a bit weird to pass back null. Maybe
|
||||
// the resulting promise should be a Promise<bool> that
|
||||
// indicates whether or not we successfully got the enqueued
|
||||
// element through the queue?
|
||||
result.complete(null);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Before returning the resulting promise, we make sure to process
|
||||
// any fully resolved enqueued elements.
|
||||
process();
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool isEmpty() {
|
||||
return (_queue === null) ? true : _queue.isEmpty();
|
||||
}
|
||||
|
||||
static void process() {
|
||||
if (_queue === null) {
|
||||
return;
|
||||
}
|
||||
while (!_queue.isEmpty() && (_queue.first())()) {
|
||||
_queue.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
static Queue<Function> _queue;
|
||||
|
||||
}
|
||||
|
||||
@@ -143,3 +143,20 @@ class Dispatcher<T> {
|
||||
T target;
|
||||
|
||||
}
|
||||
|
||||
// When a promise is sent across a port, it is converted to a
|
||||
// Promise<SendPort> down which we must send a port to receive the
|
||||
// completion value. Hand the Promise<SendPort> to this class to deal
|
||||
// with it.
|
||||
|
||||
class PromiseProxy<T> extends PromiseImpl<T> {
|
||||
PromiseProxy(Promise<SendPort> sendCompleter) {
|
||||
ReceivePort completer = new ReceivePort.singleShot();
|
||||
completer.receive((var msg, SendPort _) {
|
||||
complete(msg[0]);
|
||||
});
|
||||
sendCompleter.addCompleteHandler((SendPort port) {
|
||||
port.send([completer.toSendPort()], null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ class ReceivePortImpl implements ReceivePort {
|
||||
ReceivePort port = _portMap[id];
|
||||
SendPort replyTo = (replyId == 0) ? null : new SendPortImpl(replyId);
|
||||
(port._onMessage)(message, replyTo);
|
||||
PromiseQueue.process();
|
||||
}
|
||||
|
||||
// Call into the VM to close the VM maintained mappings.
|
||||
@@ -89,17 +88,7 @@ class ReceivePortSingleShotImpl implements ReceivePort {
|
||||
class SendPortImpl implements SendPort {
|
||||
/*--- public interface ---*/
|
||||
void send(var message, [SendPort replyTo = null]) {
|
||||
if (PromiseQueue.isEmpty()) {
|
||||
this._sendNow(message, replyTo);
|
||||
} else {
|
||||
_enqueueSend(message, replyTo);
|
||||
}
|
||||
}
|
||||
|
||||
void _enqueueSend(var message, SendPort replyTo) {
|
||||
PromiseQueue.enqueue(const []).then((ignored) {
|
||||
this._sendNow(message, replyTo);
|
||||
});
|
||||
this._sendNow(message, replyTo);
|
||||
}
|
||||
|
||||
void _sendNow(var message, SendPort replyTo) {
|
||||
|
||||
@@ -26,7 +26,7 @@ interface Purse {
|
||||
|
||||
int queryBalance();
|
||||
Purse sproutPurse();
|
||||
void deposit(int amount, Purse$Proxy source);
|
||||
int deposit(int amount, Purse$Proxy source);
|
||||
|
||||
}
|
||||
|
||||
@@ -43,12 +43,14 @@ class PurseImpl implements Purse {
|
||||
return _mint.createPurse(0);
|
||||
}
|
||||
|
||||
void deposit(int amount, Purse$Proxy purse) {
|
||||
int deposit(int amount, Purse$Proxy purse) {
|
||||
Purse$ProxyImpl impl = purse.dynamic; // TODO: Get rid of this 'cast'.
|
||||
PurseImpl source = impl.local;
|
||||
if (source._balance < amount) throw "Not enough dough.";
|
||||
_balance += amount;
|
||||
source._balance -= amount;
|
||||
print("Moved $amount, leaving ${source._balance}");
|
||||
return _balance;
|
||||
}
|
||||
|
||||
Mint _mint;
|
||||
@@ -67,15 +69,25 @@ class MintMakerPromiseTest {
|
||||
Purse$Proxy sprouted = purse.sproutPurse();
|
||||
expectEquals(0, sprouted.queryBalance());
|
||||
|
||||
sprouted.deposit(5, purse);
|
||||
expectEquals(0 + 5, sprouted.queryBalance());
|
||||
expectEquals(100 - 5, purse.queryBalance());
|
||||
Promise<int> balance = sprouted.deposit(5, purse);
|
||||
expectEquals(0 + 5, balance);
|
||||
// FIXME(benl): because we have no ordering constraints we have to
|
||||
// manually order the messages or it all falls apart. We should
|
||||
// implement E-ORDER.
|
||||
balance.addCompleteHandler((_) {
|
||||
expectEquals(0 + 5, sprouted.queryBalance());
|
||||
expectEquals(100 - 5, purse.queryBalance());
|
||||
|
||||
sprouted.deposit(42, purse);
|
||||
expectEquals(0 + 5 + 42, sprouted.queryBalance());
|
||||
expectEquals(100 - 5 - 42, purse.queryBalance());
|
||||
balance = sprouted.deposit(42, purse);
|
||||
expectEquals(0 + 5 + 42, balance);
|
||||
balance.addCompleteHandler((_) {
|
||||
expectEquals(0 + 5 + 42, sprouted.queryBalance());
|
||||
expectEquals(100 - 5 - 42, purse.queryBalance());
|
||||
// FIXME(benl): once more we could "pass" by not running anything much.
|
||||
expectDone(8);
|
||||
});
|
||||
});
|
||||
|
||||
expectDone(6);
|
||||
}
|
||||
|
||||
static Mint$Proxy createMint() {
|
||||
@@ -91,6 +103,7 @@ class MintMakerPromiseTest {
|
||||
results = new List<Promise>();
|
||||
}
|
||||
results.add(promise.then((int actual) {
|
||||
print("done $expected/$actual");
|
||||
Expect.equals(expected, actual);
|
||||
}));
|
||||
}
|
||||
@@ -102,6 +115,7 @@ class MintMakerPromiseTest {
|
||||
Promise done = new Promise();
|
||||
done.waitFor(results, results.length);
|
||||
done.then((ignored) {
|
||||
print("expectDone $n/${results.length}");
|
||||
Expect.equals(n, results.length);
|
||||
});
|
||||
}
|
||||
@@ -166,7 +180,7 @@ interface Purse$Proxy {
|
||||
|
||||
Promise<int> queryBalance();
|
||||
Purse$Proxy sproutPurse();
|
||||
void deposit(int amount, Purse$Proxy source); // Promise<int> amount.
|
||||
Promise<int> deposit(int amount, Purse$Proxy source); // Promise<int> amount.
|
||||
|
||||
}
|
||||
|
||||
@@ -179,8 +193,8 @@ class Purse$ProxyImpl extends Proxy implements Purse$Proxy {
|
||||
return this.call(["balance"]);
|
||||
}
|
||||
|
||||
void deposit(int amount, Purse$Proxy source) {
|
||||
this.send(["deposit", amount, source]);
|
||||
Promise<int> deposit(int amount, Purse$Proxy source) {
|
||||
return this.call(["deposit", amount, source]);
|
||||
}
|
||||
|
||||
Purse$Proxy sproutPurse() {
|
||||
@@ -196,14 +210,19 @@ class Purse$Dispatcher extends Dispatcher<Purse> {
|
||||
|
||||
void process(var message, void reply(var response)) {
|
||||
String command = message[0];
|
||||
print("command $command");
|
||||
if (command == "balance") {
|
||||
int balance = target.queryBalance();
|
||||
reply(balance);
|
||||
} else if (command == "deposit") {
|
||||
int amount = message[1];
|
||||
Promise<SendPort> port = new Promise<SendPort>.fromValue(message[2]);
|
||||
Purse$Proxy source = new Purse$ProxyImpl(port);
|
||||
target.deposit(amount, source);
|
||||
Promise<SendPort> port =
|
||||
new PromiseProxy<SendPort>(new Promise<SendPort>.fromValue(message[2]));
|
||||
port.addCompleteHandler((_) {
|
||||
Purse$Proxy source = new Purse$ProxyImpl(port);
|
||||
int balance = target.deposit(amount, source);
|
||||
reply(balance);
|
||||
});
|
||||
} else if (command == "sprout") {
|
||||
Purse purse = target.sproutPurse();
|
||||
SendPort port = Dispatcher.serve(new Purse$Dispatcher(purse));
|
||||
@@ -212,6 +231,7 @@ class Purse$Dispatcher extends Dispatcher<Purse> {
|
||||
// TODO: Send an exception back.
|
||||
reply("Exception: Command not understood");
|
||||
}
|
||||
print("command $command done");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,12 +12,15 @@ class TestIsolate extends Isolate {
|
||||
void main() {
|
||||
int seed = 0;
|
||||
this.port.receive((var message, SendPort replyTo) {
|
||||
print("Got ${message[0]}");
|
||||
if (seed == 0) {
|
||||
seed = message[0];
|
||||
} else {
|
||||
Promise<int> response = new Promise<int>();
|
||||
var proxy = new Proxy.forPort(replyTo);
|
||||
print("send to proxy");
|
||||
proxy.send([response]);
|
||||
print("sent");
|
||||
response.complete(seed + message[0]);
|
||||
this.port.close();
|
||||
}
|
||||
@@ -29,16 +32,45 @@ class TestIsolate extends Isolate {
|
||||
void test(TestExpectation expect) {
|
||||
Proxy proxy = new Proxy.forIsolate(new TestIsolate());
|
||||
proxy.send([42]); // Seed the isolate.
|
||||
Promise promise = expect.completes(proxy.call([87])).then((int value) {
|
||||
Promise<int> result = new PromiseProxy<int>(proxy.call([87]));
|
||||
Promise promise = expect.completes(result).then((int value) {
|
||||
print("expect 1: $value");
|
||||
Expect.equals(42 + 87, value);
|
||||
return 99;
|
||||
});
|
||||
expect.completes(promise).then((int value) {
|
||||
print("expect 2: $value");
|
||||
Expect.equals(99, value);
|
||||
expect.succeeded();
|
||||
});
|
||||
}
|
||||
|
||||
void expandedTest(TestExpectation expect) {
|
||||
Proxy proxy = new Proxy.forIsolate(new TestIsolate());
|
||||
proxy.send([42]); // Seed the isolate.
|
||||
Promise<SendPort> sendCompleter = proxy.call([87]);
|
||||
Promise<int> result = new Promise<int>();
|
||||
ReceivePort completer = new ReceivePort.singleShot();
|
||||
completer.receive((var msg, SendPort _) {
|
||||
print("test completer");
|
||||
result.complete(msg[0]);
|
||||
});
|
||||
sendCompleter.addCompleteHandler((SendPort port) {
|
||||
print("test send");
|
||||
port.send([completer.toSendPort()], null);
|
||||
});
|
||||
Promise promise = expect.completes(result).then((int value) {
|
||||
print("expect 1: $value");
|
||||
Expect.equals(42 + 87, value);
|
||||
return 99;
|
||||
});
|
||||
expect.completes(promise).then((int value) {
|
||||
print("expect 2: $value");
|
||||
Expect.equals(99, value);
|
||||
expect.succeeded();
|
||||
});
|
||||
}
|
||||
|
||||
void main() {
|
||||
runTests([test]);
|
||||
runTests([test, expandedTest]);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
typedef void AsynchronousTestFunction(TestExpectation check);
|
||||
|
||||
|
||||
void runTests(List tests) {
|
||||
TestRunner runner = new TestRunner(new TestSuite(tests));
|
||||
void runTests(List tests, [bool guarded = true]) {
|
||||
TestRunner runner = new TestRunner(new TestSuite(tests), guarded);
|
||||
TestResult result = new TestResult(runner);
|
||||
runner.run(result);
|
||||
}
|
||||
@@ -87,6 +87,9 @@ class TestResult {
|
||||
}
|
||||
|
||||
runGuarded(TestCase testCase, Function fn) {
|
||||
if (!runner.guarded) {
|
||||
return fn();
|
||||
}
|
||||
var result = null;
|
||||
try {
|
||||
result = fn();
|
||||
@@ -113,7 +116,7 @@ class TestResult {
|
||||
|
||||
class TestRunner {
|
||||
|
||||
TestRunner(this.suite);
|
||||
TestRunner(this.suite, this.guarded);
|
||||
|
||||
void run(TestResult result) {
|
||||
if (waitForDoneCallback !== null) {
|
||||
@@ -150,6 +153,7 @@ class TestRunner {
|
||||
}
|
||||
|
||||
final TestSuite suite;
|
||||
final bool guarded;
|
||||
static Function waitForDoneCallback;
|
||||
static Function doneCallback;
|
||||
|
||||
|
||||
@@ -30,9 +30,61 @@ interface Mint factory MintImpl {
|
||||
PowerfulPurse$Proxy promote(Purse$Proxy purse);
|
||||
}
|
||||
|
||||
// Because promises can't be used as keys in maps until they have
|
||||
// completed, provide a wrapper. Note that if any key promise fails to
|
||||
// resolve, then get()'s return may also fail to resolve. Also,
|
||||
// although the logic is fine, this can't be used for a
|
||||
// ProxyMap. Perhaps both Proxy and Promise should inherit from
|
||||
// Completable?
|
||||
// NB: not tested and known to be buggy. Will fix in a future change.
|
||||
class PromiseMap<S extends Promise, T> {
|
||||
|
||||
PromiseMap() {
|
||||
_map = new Map<S, T>();
|
||||
_incomplete = new Set<S>();
|
||||
}
|
||||
|
||||
T add(S s, T t) {
|
||||
_incomplete.add(s);
|
||||
s.addCompleteHandler((_) {
|
||||
_map[s] = t;
|
||||
_incomplete.remove(s);
|
||||
});
|
||||
return t;
|
||||
}
|
||||
|
||||
Promise<T> find(S s) {
|
||||
T t = _map[s];
|
||||
if (t != null)
|
||||
return new Promise<T>.fromValue(t);
|
||||
Promise<T> p = new Promise<T>();
|
||||
int counter = _incomplete.length;
|
||||
p.join(_incomplete, bool (S completed) {
|
||||
if (completed != s) {
|
||||
if (--counter == 0) {
|
||||
p.complete(null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
p.complete(_map[s]);
|
||||
return true;
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
Set<S> _incomplete;
|
||||
Map<S, T> _map;
|
||||
|
||||
}
|
||||
|
||||
class MintImpl implements Mint {
|
||||
|
||||
MintImpl() { print('mint'); }
|
||||
MintImpl() {
|
||||
print('mint');
|
||||
if (_power == null)
|
||||
_power = new Map<Purse$Proxy, PowerfulPurse$Proxy>();
|
||||
}
|
||||
|
||||
Purse$Proxy createPurse(int balance) {
|
||||
print('createPurse');
|
||||
@@ -42,17 +94,18 @@ class MintImpl implements Mint {
|
||||
purse.init(thisProxy, balance);
|
||||
|
||||
Purse$Proxy weakPurse = purse.weak();
|
||||
if (_power === null)
|
||||
_power = new Map<Purse$Proxy, PowerfulPurse$Proxy>();
|
||||
print('cP1');
|
||||
print(weakPurse.hashCode());
|
||||
_power[weakPurse] = purse;
|
||||
print('cP2');
|
||||
weakPurse.addCompleteHandler(() {
|
||||
print('cP1');
|
||||
_power[weakPurse] = purse;
|
||||
print('cP2');
|
||||
});
|
||||
return weakPurse;
|
||||
}
|
||||
|
||||
PowerfulPurse$Proxy promote(Purse$Proxy purse) {
|
||||
print('promote');
|
||||
// FIXME(benl): we should be using a PromiseMap here. But we get
|
||||
// away with it in this test for now.
|
||||
print('promote $purse/${_power[purse]}');
|
||||
return _power[purse];
|
||||
}
|
||||
|
||||
@@ -128,16 +181,16 @@ class MintMakerFullyIsolatedTest {
|
||||
done.then((int) {
|
||||
expectEquals(0 + 5, sprouted.queryBalance());
|
||||
expectEquals(100 - 5, purse.queryBalance());
|
||||
});
|
||||
|
||||
done = sprouted.deposit(42, purse);
|
||||
expectEquals(42, done);
|
||||
done.then((int) {
|
||||
expectEquals(0 + 5 + 42, sprouted.queryBalance());
|
||||
expectEquals(100 - 5 - 42, purse.queryBalance());
|
||||
});
|
||||
done = sprouted.deposit(42, purse);
|
||||
expectEquals(5 + 42, done);
|
||||
done.then((int) {
|
||||
expectEquals(0 + 5 + 42, sprouted.queryBalance());
|
||||
expectEquals(100 - 5 - 42, purse.queryBalance());
|
||||
|
||||
expectDone(8);
|
||||
expectDone(8);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static List<Promise> results;
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Purse factory PurseImpl {
|
||||
|
||||
int queryBalance();
|
||||
Purse sproutPurse();
|
||||
void deposit(int amount, Purse$Proxy source);
|
||||
int deposit(int amount, Purse$Proxy source);
|
||||
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class PurseImpl implements Purse {
|
||||
return _mint.createPurse(0);
|
||||
}
|
||||
|
||||
void deposit(int amount, Purse$Proxy proxy) {
|
||||
int deposit(int amount, Purse$Proxy proxy) {
|
||||
if (amount < 0) throw "Ha ha";
|
||||
// Because we are in the same isolate as the other purse, we can
|
||||
// retrieve the proxy's local PurseImpl object and act on it
|
||||
@@ -65,6 +65,7 @@ class PurseImpl implements Purse {
|
||||
if (source._balance < amount) throw "Not enough dough.";
|
||||
_balance += amount;
|
||||
source._balance -= amount;
|
||||
return _balance;
|
||||
}
|
||||
|
||||
Mint _mint;
|
||||
@@ -82,15 +83,22 @@ class MintMakerPromiseWithStubsTest {
|
||||
Purse$Proxy sprouted = purse.sproutPurse();
|
||||
expectEquals(0, sprouted.queryBalance());
|
||||
|
||||
sprouted.deposit(5, purse);
|
||||
expectEquals(0 + 5, sprouted.queryBalance());
|
||||
expectEquals(100 - 5, purse.queryBalance());
|
||||
// FIXME(benl): We should not have to manually order the calls
|
||||
// like this.
|
||||
Promise<int> result = sprouted.deposit(5, purse);
|
||||
expectEquals(5, result);
|
||||
result.addCompleteHandler((_) {
|
||||
expectEquals(0 + 5, sprouted.queryBalance());
|
||||
expectEquals(100 - 5, purse.queryBalance());
|
||||
|
||||
sprouted.deposit(42, purse);
|
||||
expectEquals(0 + 5 + 42, sprouted.queryBalance());
|
||||
expectEquals(100 - 5 - 42, purse.queryBalance());
|
||||
|
||||
expectDone(6);
|
||||
result = sprouted.deposit(42, purse);
|
||||
expectEquals(5 + 42, result);
|
||||
result.addCompleteHandler((_) {
|
||||
expectEquals(0 + 5 + 42, sprouted.queryBalance());
|
||||
expectEquals(100 - 5 - 42, purse.queryBalance());
|
||||
expectDone(8);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static List<Promise> results;
|
||||
@@ -100,6 +108,7 @@ class MintMakerPromiseWithStubsTest {
|
||||
results = new List<Promise>();
|
||||
}
|
||||
results.add(promise.then((int actual) {
|
||||
print("done $expected/$actual");
|
||||
Expect.equals(expected, actual);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
prefix stub-generator
|
||||
|
||||
[ $arch == ia32 ]
|
||||
MintMakerFullyIsolatedTest: Skip # Bug 5283149
|
||||
MintMakerPromiseWithStubsTest: Skip # Bug 5384756
|
||||
MintMakerFullyIsolatedTest: Skip # issue 115
|
||||
|
||||
[ $arch == dartc ]
|
||||
MintMakerFullyIsolatedTest: Fail # benl
|
||||
|
||||
[ $arch == x64 ]
|
||||
*: Skip
|
||||
|
||||
@@ -79,6 +79,7 @@ class DartStubTestConfiguration(test_configuration.StandardTestConfiguration):
|
||||
def ListTests(self, current_path, path, mode, arch):
|
||||
dartc = self.context.GetDartC(mode, 'dartc')
|
||||
if not os.access(dartc[0], os.X_OK):
|
||||
print "Can't find dartc at", str(dartc) + ", skipping"
|
||||
return []
|
||||
tests = []
|
||||
for root, dirs, files in os.walk(join(self.root, 'src')):
|
||||
|
||||
+2
-1
@@ -612,7 +612,8 @@ class Context(object):
|
||||
def GetDartC(self, mode, arch):
|
||||
"""Returns the path to the Dart --> JS compiler."""
|
||||
dartc = os.path.abspath(os.path.join(
|
||||
self.GetBuildRoot(mode, arch), 'compiler', 'bin', 'dartc'))
|
||||
utils.GetBaseDir(), 'compiler', self.GetBuildRoot(mode, arch),
|
||||
'compiler', 'bin', 'dartc'))
|
||||
if utils.IsWindows(): dartc += '.exe'
|
||||
command = [dartc]
|
||||
|
||||
|
||||
@@ -134,6 +134,9 @@ def GetBuildConf(mode, arch):
|
||||
# instead of 'dartc'.
|
||||
RUN_FROM_TOP_DIR = os.path.basename(os.path.abspath(os.curdir)) == 'dart'
|
||||
ARCH_GUESS = GuessArchitecture()
|
||||
BASE_DIR = os.path.abspath(os.path.join(os.curdir, '..'))
|
||||
if RUN_FROM_TOP_DIR:
|
||||
BASE_DIR = os.path.abspath(os.curdir)
|
||||
|
||||
def GetBuildRoot(target_os, mode=None, arch=None):
|
||||
if arch == 'dartc' and RUN_FROM_TOP_DIR: arch = ARCH_GUESS
|
||||
@@ -143,6 +146,8 @@ def GetBuildRoot(target_os, mode=None, arch=None):
|
||||
else:
|
||||
return BUILD_ROOT[target_os]
|
||||
|
||||
def GetBaseDir():
|
||||
return BASE_DIR
|
||||
|
||||
def RewritePathSeparator(path, workspace):
|
||||
# Paths in test files are always specified using '/'
|
||||
|
||||
Reference in New Issue
Block a user