Bug 1418048 - Add a callback-based Send API to async returning IPDL methods, r=billm

Currently if you write an async IPDL method which has a return value, we expose
a SendXXX method which returns a MozPromise. This MozPromise can then be
->Then-ed to run code when it is resolved or rejected.

Unfortunately, using this API loses ordering guarantees which IPDL provides.
MozPromise::Then takes an event target, which the resolve runnable is dispatched
to. This means that the resolve callback's code doesn't have any ordering
guarantees relative to the processing of other IPC messages coming over the same
protocol.

This adds a new overload to SendXXX with two additional arguments, a lambda
callback which is called if the call succeeds, and a lambda callback which is
called if the call fails. These will be called in order with other IPC messages
sent over the same protocol.

MozReview-Commit-ID: FZHJJaSDoZy
This commit is contained in:
Nika Layzell 2017-11-16 15:12:06 -05:00
parent 334a5602dc
commit a7666fd8fe
10 changed files with 265 additions and 158 deletions

View File

@ -128,7 +128,7 @@ CacheStreamControlChild::OpenStream(const nsID& aId, InputStreamResolver&& aReso
[aResolver, holder](const OptionalIPCStream& aOptionalStream) {
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aOptionalStream);
aResolver(Move(stream));
}, [aResolver, holder](PromiseRejectReason aReason) {
}, [aResolver, holder](ResponseRejectReason aReason) {
aResolver(nullptr);
});
}

View File

@ -484,9 +484,9 @@ private:
UniquePtr<IPC::Message> mReply;
};
class PromiseReporter final : public nsIMemoryReporter
class PendingResponseReporter final : public nsIMemoryReporter
{
~PromiseReporter() {}
~PendingResponseReporter() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -495,15 +495,15 @@ public:
bool aAnonymize) override
{
MOZ_COLLECT_REPORT(
"unresolved-ipc-promises", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedPromises,
"Outstanding IPC async message promises that is still not resolved.");
"unresolved-ipc-responses", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedResponses,
"Outstanding IPC async message responses that are still not resolved.");
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(PromiseReporter, nsIMemoryReporter)
NS_IMPL_ISUPPORTS(PendingResponseReporter, nsIMemoryReporter)
Atomic<size_t> MessageChannel::gUnresolvedPromises;
Atomic<size_t> MessageChannel::gUnresolvedResponses;
MessageChannel::MessageChannel(const char* aName,
IToplevelProtocol *aListener)
@ -554,7 +554,7 @@ MessageChannel::MessageChannel(const char* aName,
static Atomic<bool> registered;
if (registered.compareExchange(false, true)) {
RegisterStrongMemoryReporter(new PromiseReporter());
RegisterStrongMemoryReporter(new PendingResponseReporter());
}
}
@ -717,13 +717,11 @@ MessageChannel::Clear()
mWorkerLoop->RemoveDestructionObserver(this);
}
gUnresolvedPromises -= mPendingPromises.size();
for (auto& pair : mPendingPromises) {
pair.second.mRejectFunction(pair.second.mPromise,
PromiseRejectReason::ChannelClosed,
__func__);
gUnresolvedResponses -= mPendingResponses.size();
for (auto& pair : mPendingResponses) {
pair.second.get()->Reject(ResponseRejectReason::ChannelClosed);
}
mPendingPromises.clear();
mPendingResponses.clear();
mWorkerLoop = nullptr;
delete mLink;
@ -951,36 +949,33 @@ MessageChannel::StopPostponingSends()
mPostponedSends.clear();
}
already_AddRefed<MozPromiseRefcountable>
MessageChannel::PopPromise(const Message& aMsg)
UniquePtr<MessageChannel::UntypedCallbackHolder>
MessageChannel::PopCallback(const Message& aMsg)
{
auto iter = mPendingPromises.find(aMsg.seqno());
if (iter != mPendingPromises.end()) {
PromiseHolder ret = iter->second;
mPendingPromises.erase(iter);
gUnresolvedPromises--;
return ret.mPromise.forget();
auto iter = mPendingResponses.find(aMsg.seqno());
if (iter != mPendingResponses.end()) {
UniquePtr<MessageChannel::UntypedCallbackHolder> ret = Move(iter->second);
mPendingResponses.erase(iter);
gUnresolvedResponses--;
return ret;
}
return nullptr;
}
void
MessageChannel::RejectPendingPromisesForActor(ActorIdType aActorId)
MessageChannel::RejectPendingResponsesForActor(ActorIdType aActorId)
{
auto itr = mPendingPromises.begin();
while (itr != mPendingPromises.end()) {
if (itr->second.mActorId != aActorId) {
auto itr = mPendingResponses.begin();
while (itr != mPendingResponses.end()) {
if (itr->second.get()->mActorId != aActorId) {
++itr;
continue;
}
auto& promise = itr->second.mPromise;
itr->second.mRejectFunction(promise,
PromiseRejectReason::ActorDestroyed,
__func__);
itr->second.get()->Reject(ResponseRejectReason::ActorDestroyed);
// Take special care of advancing the iterator since we are
// removing it while iterating.
itr = mPendingPromises.erase(itr);
gUnresolvedPromises--;
itr = mPendingResponses.erase(itr);
gUnresolvedResponses--;
}
}

View File

@ -66,7 +66,7 @@ enum class SyncSendError {
ReplyError,
};
enum class PromiseRejectReason {
enum class ResponseRejectReason {
SendError,
ChannelClosed,
HandlerRejected,
@ -74,6 +74,11 @@ enum class PromiseRejectReason {
EndGuard_,
};
template<typename T>
using ResolveCallback = std::function<void (T&&)>;
using RejectCallback = std::function<void (ResponseRejectReason)>;
enum ChannelState {
ChannelClosed,
ChannelOpening,
@ -100,20 +105,45 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
// identifier is already good enough.
typedef void* ActorIdType;
struct PromiseHolder
public:
struct UntypedCallbackHolder
{
RefPtr<MozPromiseRefcountable> mPromise;
UntypedCallbackHolder(ActorIdType aActorId,
RejectCallback aReject)
: mActorId(aActorId)
, mReject(Move(aReject))
{}
virtual ~UntypedCallbackHolder() {}
void Reject(ResponseRejectReason aReason) {
mReject(aReason);
}
// For rejecting and removing the pending promises when a
// subprotocol is destoryed.
ActorIdType mActorId;
std::function<void(MozPromiseRefcountable*,
PromiseRejectReason,
const char*)> mRejectFunction;
RejectCallback mReject;
};
static Atomic<size_t> gUnresolvedPromises;
friend class PromiseReporter;
template<typename Value>
struct CallbackHolder : public UntypedCallbackHolder
{
CallbackHolder(ActorIdType aActorId,
ResolveCallback<Value> aResolve,
RejectCallback aReject)
: UntypedCallbackHolder(aActorId, Move(aReject))
, mResolve(Move(aResolve))
{}
void Resolve(Value&& aReason) {
mResolve(Move(aReason));
}
ResolveCallback<Value> mResolve;
};
private:
static Atomic<size_t> gUnresolvedResponses;
friend class PendingResponseReporter;
public:
static const int32_t kNoTimeout;
@ -188,25 +218,24 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
bool Send(Message* aMsg);
// Asynchronously send a message to the other side of the channel
// and wait for asynchronous reply
template<typename Promise>
bool Send(Message* aMsg, Promise* aPromise, ActorIdType aActorId) {
// and wait for asynchronous reply.
template<typename Value>
void Send(Message* aMsg,
ActorIdType aActorId,
ResolveCallback<Value> aResolve,
RejectCallback aReject) {
int32_t seqno = NextSeqno();
aMsg->set_seqno(seqno);
if (!Send(aMsg)) {
return false;
aReject(ResponseRejectReason::SendError);
return;
}
PromiseHolder holder;
holder.mPromise = aPromise;
holder.mActorId = aActorId;
holder.mRejectFunction = [](MozPromiseRefcountable* aRejectPromise,
PromiseRejectReason aReason,
const char* aRejectSite) {
static_cast<Promise*>(aRejectPromise)->Reject(aReason, aRejectSite);
};
mPendingPromises.insert(std::make_pair(seqno, Move(holder)));
gUnresolvedPromises++;
return true;
UniquePtr<UntypedCallbackHolder> callback =
MakeUnique<CallbackHolder<Value>>(
aActorId, Move(aResolve), Move(aReject));
mPendingResponses.insert(std::make_pair(seqno, Move(callback)));
gUnresolvedResponses++;
}
void SendBuildID();
@ -226,12 +255,12 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
bool CanSend() const;
// Remove and return a promise that needs reply
already_AddRefed<MozPromiseRefcountable> PopPromise(const Message& aMsg);
// Remove and return a callback that needs reply
UniquePtr<UntypedCallbackHolder> PopCallback(const Message& aMsg);
// Used to reject and remove pending promises owned by the given
// Used to reject and remove pending responses owned by the given
// actor when it's about to be destroyed.
void RejectPendingPromisesForActor(ActorIdType aActorId);
void RejectPendingResponsesForActor(ActorIdType aActorId);
// If sending a sync message returns an error, this function gives a more
// descriptive error message.
@ -581,7 +610,7 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
typedef LinkedList<RefPtr<MessageTask>> MessageQueue;
typedef std::map<size_t, Message> MessageMap;
typedef std::map<size_t, PromiseHolder> PromiseMap;
typedef std::map<size_t, UniquePtr<UntypedCallbackHolder>> CallbackMap;
typedef IPC::Message::msgid_t msgid_t;
void WillDestroyCurrentMessageLoop() override;
@ -792,8 +821,8 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
// https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
MessageMap mOutOfTurnReplies;
// Map of async Promises that are still waiting replies.
PromiseMap mPendingPromises;
// Map of async Callbacks that are still waiting replies.
CallbackMap mPendingResponses;
// Stack of Interrupt in-calls that were deferred because of race
// conditions.
@ -835,10 +864,10 @@ CancelCPOWs();
namespace IPC {
template <>
struct ParamTraits<mozilla::ipc::PromiseRejectReason>
: public ContiguousEnumSerializer<mozilla::ipc::PromiseRejectReason,
mozilla::ipc::PromiseRejectReason::SendError,
mozilla::ipc::PromiseRejectReason::EndGuard_>
struct ParamTraits<mozilla::ipc::ResponseRejectReason>
: public ContiguousEnumSerializer<mozilla::ipc::ResponseRejectReason,
mozilla::ipc::ResponseRejectReason::SendError,
mozilla::ipc::ResponseRejectReason::EndGuard_>
{ };
} // namespace IPC

View File

@ -342,7 +342,7 @@ def _makePromise(returns, side, resolver=False):
needmove = not all(d.isCopyable() for d in returns)
return _promise(resolvetype,
_PromiseRejectReason.Type(),
_ResponseRejectReason.Type(),
ExprLiteral.TRUE if needmove else ExprLiteral.FALSE,
resolver=resolver)
@ -517,15 +517,15 @@ class _DestroyReason:
AbnormalShutdown = ExprVar('AbnormalShutdown')
FailedConstructor = ExprVar('FailedConstructor')
class _PromiseRejectReason:
class _ResponseRejectReason:
@staticmethod
def Type():
return Type('PromiseRejectReason')
return Type('ResponseRejectReason')
SendError = ExprVar('PromiseRejectReason::SendError')
ChannelClosed = ExprVar('PromiseRejectReason::ChannelClosed')
HandlerRejected = ExprVar('PromiseRejectReason::HandlerRejected')
ActorDestroyed = ExprVar('PromiseRejectReason::ActorDestroyed')
SendError = ExprVar('ResponseRejectReason::SendError')
ChannelClosed = ExprVar('ResponseRejectReason::ChannelClosed')
HandlerRejected = ExprVar('ResponseRejectReason::HandlerRejected')
ActorDestroyed = ExprVar('ResponseRejectReason::ActorDestroyed')
##-----------------------------------------------------------------------------
@ -1020,12 +1020,27 @@ class MessageDecl(ipdl.ast.MessageDecl):
def makeResolverDecl(returns):
return Decl(Type(self.resolverName(), ref=2), 'aResolve')
def makeCallbackResolveDecl(returns):
if len(returns) > 1:
resolvetype = _tuple([d.bareType(side) for d in returns])
else:
resolvetype = returns[0].bareType(side)
return Decl(Type("mozilla::ipc::ResolveCallback", T=resolvetype),
'aResolve')
def makeCallbackRejectDecl(returns):
return Decl(Type("mozilla::ipc::RejectCallback"), 'aReject')
cxxparams = [ ]
if paramsems is not None:
cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
if returnsems is 'promise' and self.returns:
pass
elif returnsems is 'callback' and self.returns:
cxxparams.extend([ makeCallbackResolveDecl(self.returns),
makeCallbackRejectDecl(self.returns) ])
elif returnsems is 'resolver' and self.returns:
cxxparams.extend([ makeResolverDecl(self.returns) ])
elif returnsems is not None:
@ -1324,8 +1339,8 @@ with some new IPDL/C++ nodes that are tuned for C++ codegen."""
'Endpoint', ['FooSide']),
Typedef(Type('mozilla::ipc::TransportDescriptor'),
'TransportDescriptor'),
Typedef(Type('mozilla::ipc::PromiseRejectReason'),
'PromiseRejectReason') ])
Typedef(Type('mozilla::ipc::ResponseRejectReason'),
'ResponseRejectReason') ])
self.protocolName = None
def visitTranslationUnit(self, tu):
@ -3146,13 +3161,13 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
if len(ptype.manages):
destroysubtree.addstmt(Whitespace.NL)
# Reject pending promises for actor before calling ActorDestroy().
rejectPendingPromiseMethod = ExprSelect(self.protocol.callGetChannel(),
'->',
'RejectPendingPromisesForActor')
destroysubtree.addstmts([ Whitespace('// Reject owning pending promises.\n',
# Reject pending responses for actor before calling ActorDestroy().
rejectPendingResponsesMethod = ExprSelect(self.protocol.callGetChannel(),
'->',
'RejectPendingResponsesForActor')
destroysubtree.addstmts([ Whitespace('// Reject owning pending responses.\n',
indent=1),
StmtExpr(ExprCall(rejectPendingPromiseMethod,
StmtExpr(ExprCall(rejectPendingResponsesMethod,
args=[ ExprVar('this') ])),
Whitespace.NL
])
@ -3863,6 +3878,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
isdtor = md.decl.type.isDtor()
decltype = md.decl.type
sendmethod = None
promisesendmethod = None
helpermethod = None
recvlbl, recvcase = None, None
@ -3890,7 +3906,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
elif isdtor:
sendmethod = self.genBlockingDtorMethod(md)
elif isasync:
sendmethod, (recvlbl, recvcase) = self.genAsyncSendMethod(md)
sendmethod, promisesendmethod, (recvlbl, recvcase) = \
self.genAsyncSendMethod(md)
else:
sendmethod = self.genBlockingSendMethod(md)
@ -3900,6 +3917,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
if sendmethod is not None:
self.cls.addstmts([ sendmethod, Whitespace.NL ])
if promisesendmethod is not None:
self.cls.addstmts([ promisesendmethod, Whitespace.NL ])
if recvcase is not None:
addRecvCase(recvlbl, recvcase)
recvlbl, recvcase = None, None
@ -4117,36 +4136,48 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
case = StmtBlock()
resolve, reason, prologue, desrej, desstmts = self.deserializeAsyncReply(
md, self.side, errfnRecv, errfnSentinel(_Result.ValuError))
ifnotpromise = StmtIf(ExprNot(ExprVar('promise')))
ifnotpromise.addifstmts(errfnRecv("Error unknown promise",
ifnocallback = StmtIf(ExprNot(ExprVar('callback')))
ifnocallback.addifstmts(errfnRecv("Error unknown callback",
_Result.ProcessingError))
promise = _makePromise(md.returns, self.side, resolver=True)
promiseptr = _makePromise(md.returns, self.side, resolver=True)
promiseptr.ptr = 1
getpromise = [ Whitespace.NL,
StmtDecl(Decl(_refptr(promise), 'promise'),
init=ExprCall(ExprSelect(ExprCall(ExprSelect(self.protocol.callGetChannel(), '->', 'PopPromise'),
args=[ self.msgvar ]),
'.', Type('downcast', T=promise)))),
ifnotpromise ]
if len(md.returns) > 1:
resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
resolvearg = ExprCall(ExprVar('MakeTuple'),
args=[ExprMove(p.var()) for p in md.returns])
else:
resolvetype = md.returns[0].bareType(self.side)
resolvearg = ExprMove(md.returns[0].var())
resolvepromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Resolve'),
args=[ resolvearg,
ExprVar('__func__')])) ]
rejectpromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Reject'),
args=[ reason, ExprVar('__func__') ])) ]
untypedcallback = Type("MessageChannel::UntypedCallbackHolder")
callbackptr = Type("MessageChannel::CallbackHolder", T=resolvetype)
callbackptr.ptr = 1
callback = ExprVar('callback')
getcallback = [ Whitespace.NL,
# Get the untyped callback object by calling PopCallback()
StmtDecl(Decl(_uniqueptr(untypedcallback), 'untypedCallback'),
init=ExprCall(ExprSelect(self.protocol.callGetChannel(),
'->', 'PopCallback'),
args=[ self.msgvar ])),
# Cast the untyped callback pointer to the correct poiner type
StmtDecl(Decl(callbackptr, callback.name),
init=ExprCast(ExprCall(ExprSelect(ExprVar('untypedCallback'),
'.', 'get')),
callbackptr,
static=1)),
ifnocallback ]
resolvecallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Resolve'),
args=[ resolvearg ])) ]
rejectcallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Reject'),
args=[ reason ])) ]
ifresolve = StmtIf(resolve)
ifresolve.addifstmts(desstmts)
ifresolve.addifstmts(resolvepromise)
ifresolve.addifstmts(resolvecallback)
ifresolve.addelsestmts(desrej)
ifresolve.addelsestmts(rejectpromise)
ifresolve.addelsestmts(rejectcallback)
case.addstmts(prologue)
case.addstmts(getpromise)
case.addstmts(getcallback)
case.addstmt(ifresolve)
case.addstmt(StmtReturn(_Result.Processed))
return (lbl, case)
@ -4163,9 +4194,17 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
+ sendstmts
+[ StmtReturn(retvar) ])
(lbl, case) = self.genRecvAsyncReplyCase(md) if md.returns else (None, None)
return method, (lbl, case)
# Add the promise overload if we need one.
if md.returns:
promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True))
stmts = self.sendAsyncWithPromise(md)
promisemethod.addstmts(stmts)
(lbl, case) = self.genRecvAsyncReplyCase(md)
else:
(promisemethod, lbl, case) = (None, None, None)
return method, promisemethod, (lbl, case)
def genBlockingSendMethod(self, md, fromActor=None):
method = MethodDefn(self.makeSendMethodDecl(md))
@ -4344,11 +4383,11 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
destructexpr = md.returns[0].var()
selfvar = ExprVar('self__')
ifactorisdead = StmtIf(ExprNot(selfvar))
ifactorisdead.addifstmts([_printWarningMessage("Not resolving promise because actor is dead."),
ifactorisdead.addifstmts([_printWarningMessage("Not resolving response because actor is dead."),
StmtReturn()])
ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
self.protocol.deadState()))
ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving promise because actor is destroyed."),
ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving response because actor is destroyed."),
StmtReturn()])
returnifactorisdead = [ ifactorisdead,
ifactorisdestroyed ]
@ -4370,15 +4409,15 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
for r in md.returns ])
resolverfn.addstmts(sendmsg)
makepromise = [ Whitespace.NL,
StmtDecl(Decl(Type.INT32, seqno.name),
init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
selfvar.name),
init=ExprVar.THIS),
StmtDecl(Decl(resolvertype, 'resolver'),
init=resolverfn) ]
return makepromise
makeresolver = [ Whitespace.NL,
StmtDecl(Decl(Type.INT32, seqno.name),
init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
selfvar.name),
init=ExprVar.THIS),
StmtDecl(Decl(resolvertype, 'resolver'),
init=resolverfn) ]
return makeresolver
def makeReply(self, md, errfn, routingId):
if routingId is None:
@ -4505,7 +4544,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
ExprAddrOf(itervar),
errfn, "'%s'" % resolve.name,
sentinelKey=resolve.name, errfnSentinel=errfnSent) ]
desrej = [ StmtDecl(Decl(_PromiseRejectReason.Type(), reason.name)),
desrej = [ StmtDecl(Decl(_ResponseRejectReason.Type(), reason.name)),
self.checkedRead(None, ExprAddrOf(reason), msgexpr,
ExprAddrOf(itervar),
errfn, "'%s'" % reason.name,
@ -4581,37 +4620,27 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
def sendAsync(self, md, msgexpr, actor=None):
sendok = ExprVar('sendok__')
retvar = sendok
if md.returns:
retpromise = ExprVar('promise__')
promise = _makePromise(md.returns, self.side, resolver=True)
promisedecl = [ Whitespace.NL,
StmtDecl(Decl(_refptr(promise), retpromise.name),
init=ExprNew(promise, args=[ExprVar('__func__')])) ]
rejectifsendok = StmtIf(ExprNot(sendok))
rejectifsendok.addifstmts(
[ StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
args=[ _PromiseRejectReason.SendError,
ExprVar('__func__') ])) ])
resolvefn = ExprVar('aResolve')
rejectfn = ExprVar('aReject')
sendargs = [ msgexpr ]
stmts = [ Whitespace.NL,
self.logMessage(md, msgexpr, 'Sending ', actor),
self.profilerLabel(md) ] + self.transition(md, actor)
stmts.append(Whitespace.NL)
# Generate the actual call expression.
send = ExprSelect(self.protocol.callGetChannel(actor), '->', 'Send')
if md.returns:
sendargs.append(ExprCall(ExprSelect(retpromise, '.', 'get')))
sendargs.append(ExprVar('this'))
stmts.extend(promisedecl)
retvar = retpromise
stmts.extend([ Whitespace.NL,
StmtDecl(Decl(Type.BOOL, sendok.name),
init=ExprCall(
ExprSelect(self.protocol.callGetChannel(actor),
'->', 'Send'),
args=sendargs)) ])
if md.returns:
stmts.append(rejectifsendok)
stmts.append(StmtExpr(ExprCall(send, args=[ msgexpr,
ExprVar('this'),
resolvefn,
rejectfn ])))
retvar = None
else:
stmts.append(StmtDecl(Decl(Type.BOOL, sendok.name),
init=ExprCall(send, args=[ msgexpr ])))
retvar = sendok
return (retvar, stmts)
@ -4638,6 +4667,42 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
])
)
def sendAsyncWithPromise(self, md):
# Create a new promise, and forward to the callback send overload.
retpromise = ExprVar('promise__')
promise = _makePromise(md.returns, self.side, resolver=True)
stmts = [ Whitespace.NL,
StmtDecl(Decl(_refptr(promise), retpromise.name),
init=ExprNew(promise, args=[ExprVar('__func__')])) ]
if len(md.returns) > 1:
resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
else:
resolvetype = md.returns[0].bareType(self.side)
resolvetype.ref = 2
resolvefn = ExprLambda([ retpromise ],
[ Decl(resolvetype, "aValue") ])
resolvefn.addstmts([
StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Resolve'),
args=[ ExprMove(ExprVar('aValue')),
ExprVar('__func__') ])),
])
rejectfn = ExprLambda([ retpromise ],
[ Decl(_ResponseRejectReason.Type(), "aReason") ])
rejectfn.addstmts([
StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
args=[ ExprVar('aReason'),
ExprVar('__func__') ])),
])
args = [ p.var() for p in md.params ] + [ resolvefn, rejectfn ]
stmts += [ Whitespace.NL,
StmtExpr(ExprCall(ExprVar(md.sendMethod().name), args=args)),
StmtReturn(retpromise) ]
return stmts
def callAllocActor(self, md, retsems, side):
return ExprCall(
_allocMethod(md.decl.type.constructedType(), side),
@ -4686,19 +4751,24 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
decl.static = 1
return decl
def makeSendMethodDecl(self, md):
def makeSendMethodDecl(self, md, promise=False):
implicit = md.decl.type.hasImplicitActorParam()
if md.decl.type.isAsync() and md.returns:
returnsems = 'promise'
rettype = _refptr(Type(md.promiseName()))
if promise:
returnsems = 'promise'
rettype = _refptr(Type(md.promiseName()))
else:
returnsems = 'callback'
rettype = Type.VOID
else:
assert not promise
returnsems = 'out'
rettype = Type.BOOL
decl = MethodDecl(
md.sendMethod().name,
params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
side=self.side, implicit=implicit),
warn_unused=(self.side == 'parent'),
warn_unused=(self.side == 'parent' and returnsems != 'callback'),
ret=rettype)
if md.decl.type.isCtor():
decl.ret = md.actorDecl().bareType(self.side)

View File

@ -31,10 +31,10 @@ TestAsyncReturnsParent::Main()
[](bool unused) {
fail("resolve handler should not be called");
},
[](PromiseRejectReason aReason) {
[](ResponseRejectReason aReason) {
// MozPromise asserts in debug build if the
// handler is not called
if (aReason != PromiseRejectReason::ChannelClosed) {
if (aReason != ResponseRejectReason::ChannelClosed) {
fail("reject with wrong reason");
}
passed("reject handler called on channel close");
@ -46,9 +46,22 @@ TestAsyncReturnsParent::Main()
} else {
fail("get one argument but has wrong value");
}
Close();
// Also try with the callback-based API.
SendPing(
[this](bool one) {
if (one) {
passed("take one argument");
} else {
fail("get one argument but has wrong value");
}
Close();
},
[](ResponseRejectReason aReason) {
fail("sending Ping");
});
},
[](PromiseRejectReason aReason) {
[](ResponseRejectReason aReason) {
fail("sending Ping");
});
}
@ -94,7 +107,7 @@ TestAsyncReturnsChild::RecvPing(PingResolver&& aResolve)
}
aResolve(true);
},
[](PromiseRejectReason aReason) {
[](ResponseRejectReason aReason) {
fail("sending Pong");
});
return IPC_OK();

View File

@ -228,7 +228,7 @@ ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener,
[self] (const FileDescriptor& fd) {
self->OnFD(fd);
},
[self] (const mozilla::ipc::PromiseRejectReason) {
[self] (const mozilla::ipc::ResponseRejectReason) {
self->OnFD(FileDescriptor());
}
);
@ -246,7 +246,7 @@ ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener,
}
self->OnStream(inputStream);
},
[self] (const mozilla::ipc::PromiseRejectReason) {
[self] (const mozilla::ipc::ResponseRejectReason) {
self->OnStream(nullptr);
}
);

View File

@ -95,7 +95,7 @@ StreamFilter::Connect()
[=] (mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) {
self->FinishConnect(Move(aEndpoint));
},
[=] (mozilla::ipc::PromiseRejectReason aReason) {
[=] (mozilla::ipc::ResponseRejectReason aReason) {
self->mActor->RecvInitialized(false);
});
} else {

View File

@ -626,7 +626,7 @@ nsProfiler::StartGathering(double aSinceTime)
[self](const nsCString& aResult) {
self->GatheredOOPProfile(aResult);
},
[self](ipc::PromiseRejectReason aReason) {
[self](ipc::ResponseRejectReason aReason) {
self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
});
}

View File

@ -35,7 +35,7 @@ public:
static mozilla::ipc::Endpoint<PProfilerChild> CreateForProcess(base::ProcessId aOtherPid);
typedef MozPromise<nsCString, PromiseRejectReason, false> SingleProcessProfilePromise;
typedef MozPromise<nsCString, ResponseRejectReason, false> SingleProcessProfilePromise;
// The following static methods can be called on any thread, but they are
// no-ops on anything other than the main thread.

View File

@ -803,7 +803,7 @@ PuppetWidget::NotifyIMEOfFocusChange(const IMENotification& aIMENotification)
[self] (IMENotificationRequests aRequests) {
self->mIMENotificationRequestsOfParent = aRequests;
},
[self] (mozilla::ipc::PromiseRejectReason aReason) {
[self] (mozilla::ipc::ResponseRejectReason aReason) {
NS_WARNING("SendNotifyIMEFocus got rejected.");
});