mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
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:
parent
334a5602dc
commit
a7666fd8fe
2
dom/cache/CacheStreamControlChild.cpp
vendored
2
dom/cache/CacheStreamControlChild.cpp
vendored
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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--;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
);
|
||||
|
@ -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 {
|
||||
|
@ -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(""));
|
||||
});
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.");
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user