mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 1313200
- Allow IPC messages to async return MozPromises. r=billm,jwwang
This patch implements async returns for IPDL using MozPromises. There are following changes: * Initialize AbstractThreads for MessageLoops * Record promises and their reject functions * When async message returns, call their resolve functions * When send error or channel close, call their reject functions * Implement "unresolved-ipc-promises" count for about:memory * Test cases See bug attachment for generated code from test cases MozReview-Commit-ID: 7xmg8gwDGaW --HG-- rename : ipc/ipdl/test/ipdl/error/AsyncReturn.ipdl => ipc/ipdl/test/ipdl/ok/AsyncReturn.ipdl extra : rebase_source : 9a5821d6c0e5f7152b8152a17a409b94e8258dc3
This commit is contained in:
parent
36e1ce5909
commit
ffab50c6f8
@ -6,17 +6,18 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ipc/MessageChannel.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
|
||||
#include "MessageLoopAbstractThreadWrapper.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/SizePrintfMacros.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsAppRunner.h"
|
||||
#include "nsAutoPtr.h"
|
||||
@ -488,6 +489,27 @@ private:
|
||||
nsAutoPtr<IPC::Message> mReply;
|
||||
};
|
||||
|
||||
class PromiseReporter final : public nsIMemoryReporter
|
||||
{
|
||||
~PromiseReporter() {}
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
NS_IMETHOD
|
||||
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
|
||||
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.");
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(PromiseReporter, nsIMemoryReporter)
|
||||
|
||||
Atomic<size_t> MessageChannel::gUnresolvedPromises;
|
||||
|
||||
MessageChannel::MessageChannel(const char* aName,
|
||||
IToplevelProtocol *aListener)
|
||||
: mName(aName),
|
||||
@ -530,6 +552,11 @@ MessageChannel::MessageChannel(const char* aName,
|
||||
mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
|
||||
#endif
|
||||
|
||||
static Atomic<bool> registered;
|
||||
if (registered.compareExchange(false, true)) {
|
||||
RegisterStrongMemoryReporter(new PromiseReporter());
|
||||
}
|
||||
}
|
||||
|
||||
MessageChannel::~MessageChannel()
|
||||
@ -672,6 +699,12 @@ MessageChannel::Clear()
|
||||
mWorkerLoop->RemoveDestructionObserver(this);
|
||||
}
|
||||
|
||||
gUnresolvedPromises -= mPendingPromises.size();
|
||||
for (auto& pair : mPendingPromises) {
|
||||
pair.second.mRejectFunction(__func__);
|
||||
}
|
||||
mPendingPromises.clear();
|
||||
|
||||
mWorkerLoop = nullptr;
|
||||
delete mLink;
|
||||
mLink = nullptr;
|
||||
@ -703,9 +736,13 @@ MessageChannel::Open(Transport* aTransport, MessageLoop* aIOLoop, Side aSide)
|
||||
mMonitor = new RefCountedMonitor();
|
||||
mWorkerLoop = MessageLoop::current();
|
||||
mWorkerLoopID = mWorkerLoop->id();
|
||||
|
||||
mWorkerLoop->AddDestructionObserver(this);
|
||||
|
||||
if (!AbstractThread::GetCurrent()) {
|
||||
mAbstractThread = MessageLoopAbstractThreadWrapper::Create(mWorkerLoop);
|
||||
}
|
||||
|
||||
|
||||
ProcessLink *link = new ProcessLink(this);
|
||||
link->Open(aTransport, aIOLoop, aSide); // :TODO: n.b.: sets mChild
|
||||
mLink = link;
|
||||
@ -783,6 +820,11 @@ MessageChannel::CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide)
|
||||
mWorkerLoop = MessageLoop::current();
|
||||
mWorkerLoopID = mWorkerLoop->id();
|
||||
mWorkerLoop->AddDestructionObserver(this);
|
||||
|
||||
if (!AbstractThread::GetCurrent()) {
|
||||
mAbstractThread = MessageLoopAbstractThreadWrapper::Create(mWorkerLoop);
|
||||
}
|
||||
|
||||
mLink = new ThreadLink(this, aTargetChan);
|
||||
mSide = aSide;
|
||||
}
|
||||
@ -851,6 +893,19 @@ MessageChannel::Send(Message* aMsg)
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<MozPromiseRefcountable>
|
||||
MessageChannel::PopPromise(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();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class BuildIDMessage : public IPC::Message
|
||||
{
|
||||
public:
|
||||
|
@ -11,8 +11,11 @@
|
||||
#include "base/basictypes.h"
|
||||
#include "base/message_loop.h"
|
||||
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#if defined(OS_WIN)
|
||||
#include "mozilla/ipc/Neutering.h"
|
||||
@ -26,10 +29,13 @@
|
||||
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <math.h>
|
||||
#include <stack>
|
||||
|
||||
namespace mozilla {
|
||||
class AbstractThread;
|
||||
|
||||
namespace ipc {
|
||||
|
||||
class MessageChannel;
|
||||
@ -61,6 +67,13 @@ enum class SyncSendError {
|
||||
ReplyError,
|
||||
};
|
||||
|
||||
enum class PromiseRejectReason {
|
||||
SendError,
|
||||
ChannelClosed,
|
||||
HandlerRejected,
|
||||
EndGuard_,
|
||||
};
|
||||
|
||||
enum ChannelState {
|
||||
ChannelClosed,
|
||||
ChannelOpening,
|
||||
@ -82,6 +95,14 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
|
||||
|
||||
typedef mozilla::Monitor Monitor;
|
||||
|
||||
struct PromiseHolder
|
||||
{
|
||||
RefPtr<MozPromiseRefcountable> mPromise;
|
||||
std::function<void(const char*)> mRejectFunction;
|
||||
};
|
||||
static Atomic<size_t> gUnresolvedPromises;
|
||||
friend class PromiseReporter;
|
||||
|
||||
public:
|
||||
static const int32_t kNoTimeout;
|
||||
|
||||
@ -154,6 +175,25 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
|
||||
// Asynchronously send a message to the other side of the channel
|
||||
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) {
|
||||
int32_t seqno = NextSeqno();
|
||||
aMsg->set_seqno(seqno);
|
||||
if (!Send(aMsg)) {
|
||||
return false;
|
||||
}
|
||||
PromiseHolder holder;
|
||||
holder.mPromise = aPromise;
|
||||
holder.mRejectFunction = [aPromise](const char* aRejectSite) {
|
||||
aPromise->Reject(PromiseRejectReason::ChannelClosed, aRejectSite);
|
||||
};
|
||||
mPendingPromises.insert(std::make_pair(seqno, Move(holder)));
|
||||
gUnresolvedPromises++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendBuildID();
|
||||
|
||||
// Asynchronously deliver a message back to this side of the
|
||||
@ -171,6 +211,9 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
|
||||
|
||||
bool CanSend() const;
|
||||
|
||||
// Remove and return a promise that needs reply
|
||||
already_AddRefed<MozPromiseRefcountable> PopPromise(const Message& aMsg);
|
||||
|
||||
// If sending a sync message returns an error, this function gives a more
|
||||
// descriptive error message.
|
||||
SyncSendError LastSendError() const {
|
||||
@ -490,6 +533,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 IPC::Message::msgid_t msgid_t;
|
||||
|
||||
void WillDestroyCurrentMessageLoop() override;
|
||||
@ -506,6 +550,7 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
|
||||
Side mSide;
|
||||
MessageLink* mLink;
|
||||
MessageLoop* mWorkerLoop; // thread where work is done
|
||||
RefPtr<AbstractThread> mAbstractThread;
|
||||
RefPtr<CancelableRunnable> mChannelErrorTask; // NotifyMaybeChannelError runnable
|
||||
|
||||
// id() of mWorkerLoop. This persists even after mWorkerLoop is cleared
|
||||
@ -520,7 +565,7 @@ class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
|
||||
bool mInTimeoutSecondHalf;
|
||||
|
||||
// Worker-thread only; sequence numbers for messages that require
|
||||
// synchronous replies.
|
||||
// replies.
|
||||
int32_t mNextSeqno;
|
||||
|
||||
static bool sIsPumpingMessages;
|
||||
@ -689,6 +734,9 @@ 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;
|
||||
|
||||
// Stack of Interrupt in-calls that were deferred because of race
|
||||
// conditions.
|
||||
std::stack<Message> mDeferred;
|
||||
@ -722,4 +770,13 @@ CancelCPOWs();
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
namespace IPC {
|
||||
template <>
|
||||
struct ParamTraits<mozilla::ipc::PromiseRejectReason>
|
||||
: public ContiguousEnumSerializer<mozilla::ipc::PromiseRejectReason,
|
||||
mozilla::ipc::PromiseRejectReason::SendError,
|
||||
mozilla::ipc::PromiseRejectReason::EndGuard_>
|
||||
{ };
|
||||
} // namespace IPC
|
||||
|
||||
#endif // ifndef ipc_glue_MessageChannel_h
|
||||
|
159
ipc/glue/MessageLoopAbstractThreadWrapper.h
Normal file
159
ipc/glue/MessageLoopAbstractThreadWrapper.h
Normal file
@ -0,0 +1,159 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_ipc_glue_MessageLoopAbstractThreadWrapper_h
|
||||
#define mozilla_ipc_glue_MessageLoopAbstractThreadWrapper_h
|
||||
|
||||
#include "mozilla/AbstractThread.h"
|
||||
|
||||
#include "base/message_loop.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class MessageLoopAbstractThreadWrapper : public AbstractThread
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<AbstractThread>
|
||||
Create(MessageLoop* aMessageLoop)
|
||||
{
|
||||
RefPtr<MessageLoopAbstractThreadWrapper> wrapper =
|
||||
new MessageLoopAbstractThreadWrapper(aMessageLoop);
|
||||
|
||||
bool onCurrentThread = (aMessageLoop == MessageLoop::current());
|
||||
|
||||
if (onCurrentThread) {
|
||||
sCurrentThreadTLS.set(wrapper);
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
// Set the thread-local sCurrentThreadTLS to point to the wrapper on the
|
||||
// target thread. This ensures that sCurrentThreadTLS is as expected by
|
||||
// AbstractThread::GetCurrent() on the target thread.
|
||||
RefPtr<Runnable> r =
|
||||
NS_NewRunnableFunction([wrapper]() { sCurrentThreadTLS.set(wrapper); });
|
||||
aMessageLoop->PostTask(r.forget());
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
|
||||
DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
|
||||
DispatchReason aReason = NormalDispatch) override
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aReason == NormalDispatch, "Only supports NormalDispatch");
|
||||
|
||||
RefPtr<Runnable> runner(new Runner(this, Move(aRunnable)));
|
||||
mMessageLoop->PostTask(runner.forget());
|
||||
}
|
||||
|
||||
virtual bool IsCurrentThreadIn() override
|
||||
{
|
||||
MessageLoop* messageLoop = MessageLoop::current();
|
||||
bool in = (mMessageLoop == messageLoop);
|
||||
return in;
|
||||
}
|
||||
|
||||
virtual TaskDispatcher& TailDispatcher() override
|
||||
{
|
||||
MOZ_CRASH("Not supported!");
|
||||
TaskDispatcher* dispatcher = nullptr;
|
||||
return *dispatcher;
|
||||
}
|
||||
|
||||
virtual bool MightHaveTailTasks() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
explicit MessageLoopAbstractThreadWrapper(MessageLoop* aMessageLoop)
|
||||
: AbstractThread(false)
|
||||
, mMessageLoop(aMessageLoop)
|
||||
{
|
||||
}
|
||||
|
||||
MessageLoop* mMessageLoop;
|
||||
|
||||
class Runner : public CancelableRunnable {
|
||||
class MOZ_STACK_CLASS AutoTaskGuard final {
|
||||
public:
|
||||
explicit AutoTaskGuard(MessageLoopAbstractThreadWrapper* aThread)
|
||||
: mLastCurrentThread(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aThread);
|
||||
mLastCurrentThread = sCurrentThreadTLS.get();
|
||||
sCurrentThreadTLS.set(aThread);
|
||||
}
|
||||
|
||||
~AutoTaskGuard()
|
||||
{
|
||||
sCurrentThreadTLS.set(mLastCurrentThread);
|
||||
}
|
||||
private:
|
||||
AbstractThread* mLastCurrentThread;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Runner(MessageLoopAbstractThreadWrapper* aThread,
|
||||
already_AddRefed<nsIRunnable> aRunnable)
|
||||
: mThread(aThread)
|
||||
, mRunnable(aRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
AutoTaskGuard taskGuard(mThread);
|
||||
|
||||
MOZ_ASSERT(mThread == AbstractThread::GetCurrent());
|
||||
MOZ_ASSERT(mThread->IsCurrentThreadIn());
|
||||
nsresult rv = mRunnable->Run();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult Cancel() override
|
||||
{
|
||||
// Set the TLS during Cancel() just in case it calls Run().
|
||||
AutoTaskGuard taskGuard(mThread);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Try to cancel the runnable if it implements the right interface.
|
||||
// Otherwise just skip the runnable.
|
||||
nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mRunnable);
|
||||
if (cr) {
|
||||
rv = cr->Cancel();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetName(nsACString& aName) override
|
||||
{
|
||||
aName.AssignLiteral("AbstractThread::Runner");
|
||||
if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
|
||||
nsAutoCString name;
|
||||
named->GetName(name);
|
||||
if (!name.IsEmpty()) {
|
||||
aName.AppendLiteral(" for ");
|
||||
aName.Append(name);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<MessageLoopAbstractThreadWrapper> mThread;
|
||||
RefPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ipc_glue_MessageLoopAbstractThreadWrapper_h
|
@ -24,6 +24,7 @@
|
||||
#include "mozilla/ipc/MessageLink.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/NotNull.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
@ -168,6 +168,9 @@ class Visitor:
|
||||
def visitExprSizeof(self, es):
|
||||
self.visitExprCall(es)
|
||||
|
||||
def visitExprLambda(self, l):
|
||||
self.visitBlock(l)
|
||||
|
||||
def visitStmtBlock(self, sb):
|
||||
self.visitBlock(sb)
|
||||
|
||||
@ -292,8 +295,12 @@ class Type(Node):
|
||||
ptr=0, ptrconst=0, ptrptr=0, ptrconstptr=0,
|
||||
ref=0,
|
||||
hasimplicitcopyctor=True,
|
||||
T=None):
|
||||
T=None,
|
||||
inner=None):
|
||||
"""
|
||||
Represents the type |name<T>::inner| with the ptr and const
|
||||
modifiers as specified.
|
||||
|
||||
To avoid getting fancy with recursive types, we limit the kinds
|
||||
of pointer types that can be be constructed.
|
||||
|
||||
@ -318,6 +325,7 @@ Any type, naked or pointer, can be const (const T) or ref (T&).
|
||||
self.ref = ref
|
||||
self.hasimplicitcopyctor = hasimplicitcopyctor
|
||||
self.T = T
|
||||
self.inner = inner
|
||||
# XXX could get serious here with recursive types, but shouldn't
|
||||
# need that for this codegen
|
||||
def __deepcopy__(self, memo):
|
||||
@ -326,7 +334,8 @@ Any type, naked or pointer, can be const (const T) or ref (T&).
|
||||
ptr=self.ptr, ptrconst=self.ptrconst,
|
||||
ptrptr=self.ptrptr, ptrconstptr=self.ptrconstptr,
|
||||
ref=self.ref,
|
||||
T=copy.deepcopy(self.T, memo))
|
||||
T=copy.deepcopy(self.T, memo),
|
||||
inner=copy.deepcopy(self.inner, memo))
|
||||
Type.BOOL = Type('bool')
|
||||
Type.INT = Type('int')
|
||||
Type.INT32 = Type('int32_t')
|
||||
@ -642,12 +651,15 @@ class ExprSelect(Node):
|
||||
def __init__(self, obj, op, field):
|
||||
assert obj and op and field
|
||||
assert not isinstance(obj, str)
|
||||
assert isinstance(field, str)
|
||||
assert isinstance(op, str)
|
||||
|
||||
Node.__init__(self)
|
||||
self.obj = obj
|
||||
self.op = op
|
||||
self.field = field
|
||||
if isinstance(field, str):
|
||||
self.field = ExprVar(field)
|
||||
else:
|
||||
self.field = field
|
||||
|
||||
class ExprAssn(Node):
|
||||
def __init__(self, lhs, rhs, op='='):
|
||||
@ -693,6 +705,15 @@ class ExprSizeof(ExprCall):
|
||||
def __init__(self, t):
|
||||
ExprCall.__init__(self, ExprVar('sizeof'), [ t ])
|
||||
|
||||
class ExprLambda(Block):
|
||||
def __init__(self, captures=[ ], params=[ ], ret=None):
|
||||
Block.__init__(self)
|
||||
assert isinstance(captures, list)
|
||||
assert isinstance(params, list)
|
||||
self.captures = captures
|
||||
self.params = params
|
||||
self.ret = ret
|
||||
|
||||
##------------------------------
|
||||
# statements etc.
|
||||
class StmtBlock(Block):
|
||||
|
@ -38,9 +38,19 @@ class CxxCodeGen(CodePrinter, Visitor):
|
||||
|
||||
if t.T is not None:
|
||||
self.write('<')
|
||||
t.T.accept(self)
|
||||
if type(t.T) is list:
|
||||
t.T[0].accept(self)
|
||||
for tt in t.T[1:]:
|
||||
self.write(', ')
|
||||
tt.accept(self)
|
||||
else:
|
||||
t.T.accept(self)
|
||||
self.write('>')
|
||||
|
||||
if t.inner is not None:
|
||||
self.write('::')
|
||||
t.inner.accept(self)
|
||||
|
||||
ts = ''
|
||||
if t.ptr: ts += '*'
|
||||
elif t.ptrconst: ts += '* const'
|
||||
@ -345,7 +355,8 @@ class CxxCodeGen(CodePrinter, Visitor):
|
||||
self.write('(')
|
||||
es.obj.accept(self)
|
||||
self.write(')')
|
||||
self.write(es.op + es.field)
|
||||
self.write(es.op)
|
||||
es.field.accept(self)
|
||||
|
||||
def visitExprAssn(self, ea):
|
||||
ea.lhs.accept(self)
|
||||
@ -377,6 +388,24 @@ class CxxCodeGen(CodePrinter, Visitor):
|
||||
self.write('delete ')
|
||||
ed.obj.accept(self)
|
||||
|
||||
def visitExprLambda(self, l):
|
||||
self.write('[')
|
||||
ncaptures = len(l.captures)
|
||||
for i, c in enumerate(l.captures):
|
||||
c.accept(self)
|
||||
if i != (ncaptures-1):
|
||||
self.write(', ')
|
||||
self.write('](')
|
||||
self.writeDeclList(l.params)
|
||||
self.write(')')
|
||||
if l.ret:
|
||||
self.write(' -> ')
|
||||
l.ret.accept(self)
|
||||
self.println(' {')
|
||||
self.indent()
|
||||
self.visitBlock(l)
|
||||
self.dedent()
|
||||
self.printdent('}')
|
||||
|
||||
def visitStmtBlock(self, b):
|
||||
self.printdentln('{')
|
||||
|
@ -308,8 +308,8 @@ def _abortIfFalse(cond, msg):
|
||||
ExprVar('MOZ_RELEASE_ASSERT'),
|
||||
[ cond, ExprLiteral.String(msg) ]))
|
||||
|
||||
def _refptr(T):
|
||||
return Type('RefPtr', T=T)
|
||||
def _refptr(T, ptr=0, ref=0):
|
||||
return Type('RefPtr', T=T, ptr=ptr, ref=ref)
|
||||
|
||||
def _refptrGet(expr):
|
||||
return ExprCall(ExprSelect(expr, '.', 'get'))
|
||||
@ -326,6 +326,22 @@ def _uniqueptr(T):
|
||||
def _uniqueptrGet(expr):
|
||||
return ExprCall(ExprSelect(expr, '.', 'get'))
|
||||
|
||||
def _tuple(types, const=0, ref=0):
|
||||
return Type('Tuple', T=types, const=const, ref=ref)
|
||||
|
||||
def _promise(resolvetype, rejecttype, tail, resolver=False):
|
||||
inner = Type('Private') if resolver else None
|
||||
return Type('MozPromise', T=[resolvetype, rejecttype, tail], inner=inner)
|
||||
|
||||
def _makePromise(returns, side, resolver=False):
|
||||
if len(returns) > 1:
|
||||
resolvetype = _tuple([d.bareType(side) for d in returns])
|
||||
else:
|
||||
resolvetype = returns[0].bareType(side)
|
||||
return _promise(resolvetype,
|
||||
_PromiseRejectReason.Type(),
|
||||
ExprLiteral.FALSE, resolver=resolver)
|
||||
|
||||
def _cxxArrayType(basetype, const=0, ref=0):
|
||||
return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)
|
||||
|
||||
@ -490,6 +506,15 @@ class _DestroyReason:
|
||||
AbnormalShutdown = ExprVar('AbnormalShutdown')
|
||||
FailedConstructor = ExprVar('FailedConstructor')
|
||||
|
||||
class _PromiseRejectReason:
|
||||
@staticmethod
|
||||
def Type():
|
||||
return Type('PromiseRejectReason')
|
||||
|
||||
SendError = ExprVar('PromiseRejectReason::SendError')
|
||||
ChannelClosed = ExprVar('PromiseRejectReason::ChannelClosed')
|
||||
HandlerRejected = ExprVar('PromiseRejectReason::HandlerRejected')
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
## Intermediate representation (IR) nodes used during lowering
|
||||
|
||||
@ -914,6 +939,10 @@ class MessageDecl(ipdl.ast.MessageDecl):
|
||||
or self.decl.type.isCtor()
|
||||
or self.decl.type.isDtor())
|
||||
|
||||
def hasAsyncReturns(self):
|
||||
return (self.decl.type.isAsync() and
|
||||
self.returns)
|
||||
|
||||
def msgCtorFunc(self):
|
||||
return 'Msg_%s'% (self.decl.progname)
|
||||
|
||||
@ -940,6 +969,13 @@ class MessageDecl(ipdl.ast.MessageDecl):
|
||||
def prettyReplyName(self, pfx=''):
|
||||
return pfx + self.replyCtorFunc()
|
||||
|
||||
def promiseName(self):
|
||||
name = self.baseName()
|
||||
if self.decl.type.isCtor():
|
||||
name += 'Constructor'
|
||||
name += 'Promise'
|
||||
return name
|
||||
|
||||
def actorDecl(self):
|
||||
return self.params[0]
|
||||
|
||||
@ -957,11 +993,19 @@ class MessageDecl(ipdl.ast.MessageDecl):
|
||||
return Decl(d.outType(side), d.name)
|
||||
else: assert 0
|
||||
|
||||
def makeResolverDecl(returns):
|
||||
return Decl(_refptr(Type(self.promiseName()), ref=2),
|
||||
'aPromise')
|
||||
|
||||
cxxparams = [ ]
|
||||
if paramsems is not None:
|
||||
cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
|
||||
|
||||
if returnsems is not None:
|
||||
if returnsems is 'promise' and self.returns:
|
||||
pass
|
||||
elif returnsems is 'resolver' and self.returns:
|
||||
cxxparams.extend([ makeResolverDecl(self.returns) ])
|
||||
elif returnsems is not None:
|
||||
cxxparams.extend([ makeDecl(r, returnsems) for r in self.returns ])
|
||||
|
||||
if not implicit and self.decl.type.hasImplicitActorParam():
|
||||
@ -994,6 +1038,10 @@ class MessageDecl(ipdl.ast.MessageDecl):
|
||||
elif retcallsems is 'out':
|
||||
cxxargs.append(ret.var())
|
||||
else: assert 0
|
||||
elif retsems is 'resolver':
|
||||
pass
|
||||
if retsems is 'resolver':
|
||||
cxxargs.append(ExprMove(ExprVar('promise')))
|
||||
|
||||
if not implicit:
|
||||
assert self.decl.type.hasImplicitActorParam()
|
||||
@ -1252,7 +1300,9 @@ with some new IPDL/C++ nodes that are tuned for C++ codegen."""
|
||||
Typedef(Type('mozilla::ipc::Endpoint'),
|
||||
'Endpoint', ['FooSide']),
|
||||
Typedef(Type('mozilla::ipc::TransportDescriptor'),
|
||||
'TransportDescriptor') ])
|
||||
'TransportDescriptor'),
|
||||
Typedef(Type('mozilla::ipc::PromiseRejectReason'),
|
||||
'PromiseRejectReason') ])
|
||||
self.protocolName = None
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
@ -2566,6 +2616,11 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
self.cls.addstmt(typedef)
|
||||
for typedef in self.includedActorTypedefs:
|
||||
self.cls.addstmt(typedef)
|
||||
for md in p.messageDecls:
|
||||
if self.receivesMessage(md) and md.hasAsyncReturns():
|
||||
self.cls.addstmt(
|
||||
Typedef(_makePromise(md.returns, self.side, resolver=True),
|
||||
md.promiseName()))
|
||||
|
||||
self.cls.addstmt(Whitespace.NL)
|
||||
|
||||
@ -2578,9 +2633,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
if self.receivesMessage(md):
|
||||
# generate Recv/Answer* interface
|
||||
implicit = (not isdtor)
|
||||
returnsems = 'resolver' if md.decl.type.isAsync() else 'out'
|
||||
recvDecl = MethodDecl(
|
||||
md.recvMethod().name,
|
||||
params=md.makeCxxParams(paramsems='move', returnsems='out',
|
||||
params=md.makeCxxParams(paramsems='move', returnsems=returnsems,
|
||||
side=self.side, implicit=implicit),
|
||||
ret=Type('mozilla::ipc::IPCResult'), virtual=1)
|
||||
|
||||
@ -3727,7 +3783,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
elif isdtor:
|
||||
sendmethod = self.genBlockingDtorMethod(md)
|
||||
elif isasync:
|
||||
sendmethod = self.genAsyncSendMethod(md)
|
||||
sendmethod, (recvlbl, recvcase) = self.genAsyncSendMethod(md)
|
||||
else:
|
||||
sendmethod = self.genBlockingSendMethod(md)
|
||||
|
||||
@ -3949,17 +4005,59 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
def dtorEpilogue(self, md, actorexpr):
|
||||
return self.destroyActor(md, actorexpr)
|
||||
|
||||
def genRecvAsyncReplyCase(self, md):
|
||||
lbl = CaseLabel(md.pqReplyId())
|
||||
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",
|
||||
_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:
|
||||
resolvearg = ExprCall(ExprVar('MakeTuple'),
|
||||
args=[p.var() for p in md.returns])
|
||||
else:
|
||||
resolvearg = 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__') ])) ]
|
||||
ifresolve = StmtIf(resolve)
|
||||
ifresolve.addifstmts(desstmts)
|
||||
ifresolve.addifstmts(resolvepromise)
|
||||
ifresolve.addelsestmts(desrej)
|
||||
ifresolve.addelsestmts(rejectpromise)
|
||||
case.addstmts(prologue)
|
||||
case.addstmts(getpromise)
|
||||
case.addstmt(ifresolve)
|
||||
case.addstmt(StmtReturn(_Result.Processed))
|
||||
return (lbl, case)
|
||||
|
||||
def genAsyncSendMethod(self, md):
|
||||
method = MethodDefn(self.makeSendMethodDecl(md))
|
||||
msgvar, stmts = self.makeMessage(md, errfnSend)
|
||||
sendok, sendstmts = self.sendAsync(md, msgvar)
|
||||
retvar, sendstmts = self.sendAsync(md, msgvar)
|
||||
|
||||
method.addstmts(stmts
|
||||
+[ Whitespace.NL ]
|
||||
+ self.genVerifyMessage(md.decl.type.verify, md.params,
|
||||
errfnSend, ExprVar('msg__'))
|
||||
+ sendstmts
|
||||
+[ StmtReturn(sendok) ])
|
||||
return method
|
||||
+[ StmtReturn(retvar) ])
|
||||
|
||||
(lbl, case) = self.genRecvAsyncReplyCase(md) if md.returns else (None, None)
|
||||
return method, (lbl, case)
|
||||
|
||||
|
||||
def genBlockingSendMethod(self, md, fromActor=None):
|
||||
@ -4058,12 +4156,15 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
errfnSent=errfnSentinel(_Result.ValuError))
|
||||
|
||||
idvar, saveIdStmts = self.saveActorId(md)
|
||||
declstmts = [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
|
||||
for r in md.returns ]
|
||||
if md.decl.type.isAsync() and md.returns:
|
||||
declstmts = self.makePromise(md, errfnRecv, routingId=idvar)
|
||||
case.addstmts(
|
||||
stmts
|
||||
+ self.transition(md)
|
||||
+ [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
|
||||
for r in md.returns ]
|
||||
+ saveIdStmts
|
||||
+ declstmts
|
||||
+ self.invokeRecvHandler(md)
|
||||
+ [ Whitespace.NL ]
|
||||
+ self.makeReply(md, errfnRecv, routingId=idvar)
|
||||
@ -4104,12 +4205,90 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return msgvar, stmts
|
||||
|
||||
|
||||
def makePromise(self, md, errfn, routingId):
|
||||
if routingId is None:
|
||||
routingId = self.protocol.routingId()
|
||||
if not md.decl.type.isAsync() or not md.hasReply():
|
||||
return [ ]
|
||||
|
||||
sendok = ExprVar('sendok__')
|
||||
seqno = ExprVar('seqno__')
|
||||
resolve = ExprVar('resolve__')
|
||||
reason = ExprVar('reason__')
|
||||
promise = Type(md.promiseName())
|
||||
failifsendok = StmtIf(ExprNot(sendok))
|
||||
failifsendok.addifstmt(_printWarningMessage('Error sending reply'))
|
||||
sendmsg = (self.setMessageFlags(md, self.replyvar, reply=1, seqno=seqno)
|
||||
+ [ self.logMessage(md, self.replyvar, 'Sending reply '),
|
||||
StmtDecl(Decl(Type.BOOL, sendok.name),
|
||||
init=ExprCall(
|
||||
ExprSelect(self.protocol.callGetChannel(),
|
||||
'->', 'Send'),
|
||||
args=[ self.replyvar ])),
|
||||
failifsendok ])
|
||||
if len(md.returns) > 1:
|
||||
resolvedecl = Decl(_tuple([p.bareType(self.side) for p in md.returns],
|
||||
const=1, ref=1),
|
||||
'aParam')
|
||||
destructexpr = ExprCall(ExprVar('Tie'),
|
||||
args=[ p.var() for p in md.returns ])
|
||||
else:
|
||||
resolvedecl = Decl(md.returns[0].bareType(self.side), 'aParam')
|
||||
destructexpr = md.returns[0].var()
|
||||
promisethen = ExprLambda([ExprVar.THIS, routingId, seqno],
|
||||
[resolvedecl])
|
||||
promisethen.addstmts([ StmtDecl(Decl(Type.BOOL, resolve.name),
|
||||
init=ExprLiteral.TRUE) ]
|
||||
+ [ StmtDecl(Decl(p.bareType(self.side), p.var().name))
|
||||
for p in md.returns ]
|
||||
+ [ StmtExpr(ExprAssn(destructexpr, ExprVar('aParam'))),
|
||||
StmtDecl(Decl(Type('IPC::Message', ptr=1), self.replyvar.name),
|
||||
init=ExprCall(ExprVar(md.pqReplyCtorFunc()),
|
||||
args=[ routingId ])) ]
|
||||
+ [ self.checkedWrite(None, resolve, self.replyvar,
|
||||
sentinelKey=resolve.name) ]
|
||||
+ [ self.checkedWrite(r.ipdltype, r.var(), self.replyvar,
|
||||
sentinelKey=r.name)
|
||||
for r in md.returns ])
|
||||
promisethen.addstmts(sendmsg)
|
||||
promiserej = ExprLambda([ExprVar.THIS, routingId, seqno],
|
||||
[Decl(_PromiseRejectReason.Type(), reason.name)])
|
||||
promiserej.addstmts([ StmtExpr(ExprCall(ExprVar('MOZ_ASSERT'),
|
||||
args=[ ExprBinary(reason, '==',
|
||||
_PromiseRejectReason.HandlerRejected) ])),
|
||||
StmtExpr(ExprAssn(reason, _PromiseRejectReason.HandlerRejected)),
|
||||
StmtDecl(Decl(Type.BOOL, resolve.name),
|
||||
init=ExprLiteral.FALSE),
|
||||
StmtDecl(Decl(Type('IPC::Message', ptr=1), self.replyvar.name),
|
||||
init=ExprCall(ExprVar(md.pqReplyCtorFunc()),
|
||||
args=[ routingId ])),
|
||||
self.checkedWrite(None, resolve, self.replyvar,
|
||||
sentinelKey=resolve.name),
|
||||
self.checkedWrite(None, reason, self.replyvar,
|
||||
sentinelKey=reason.name) ])
|
||||
promiserej.addstmts(sendmsg)
|
||||
|
||||
makepromise = [ Whitespace.NL,
|
||||
StmtDecl(Decl(Type.INT32, seqno.name),
|
||||
init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
|
||||
StmtDecl(Decl(_refptr(promise), 'promise'),
|
||||
init=ExprNew(promise, args=[ExprVar('__func__')])),
|
||||
StmtExpr(ExprCall(
|
||||
ExprSelect(ExprVar('promise'), '->', 'Then'),
|
||||
args=[ ExprCall(ExprVar('AbstractThread::GetCurrent')),
|
||||
ExprVar('__func__'),
|
||||
promisethen,
|
||||
promiserej ])) ]
|
||||
return makepromise
|
||||
|
||||
def makeReply(self, md, errfn, routingId):
|
||||
if routingId is None:
|
||||
routingId = self.protocol.routingId()
|
||||
# TODO special cases for async ctor/dtor replies
|
||||
if not md.decl.type.hasReply():
|
||||
return [ ]
|
||||
if md.decl.type.isAsync() and md.decl.type.hasReply():
|
||||
return [ ]
|
||||
|
||||
replyvar = self.replyvar
|
||||
return (
|
||||
@ -4161,7 +4340,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
return stmts
|
||||
|
||||
def setMessageFlags(self, md, var, reply):
|
||||
def setMessageFlags(self, md, var, reply, seqno=None):
|
||||
stmts = [ ]
|
||||
|
||||
if md.decl.type.isSync():
|
||||
@ -4179,6 +4358,11 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
stmts.append(StmtExpr(ExprCall(
|
||||
ExprSelect(var, '->', 'set_reply'))))
|
||||
|
||||
if seqno:
|
||||
stmts.append(StmtExpr(ExprCall(
|
||||
ExprSelect(var, '->', 'set_seqno'),
|
||||
args=[ seqno ])))
|
||||
|
||||
return stmts + [ Whitespace.NL ]
|
||||
|
||||
|
||||
@ -4225,8 +4409,65 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
return stmts
|
||||
|
||||
def deserializeAsyncReply(self, md, side, errfn, errfnSent):
|
||||
msgvar = self.msgvar
|
||||
itervar = self.itervar
|
||||
msgexpr = ExprAddrOf(msgvar)
|
||||
isctor = md.decl.type.isCtor()
|
||||
resolve = ExprVar('resolve__')
|
||||
reason = ExprVar('reason__')
|
||||
desresolve = [ StmtDecl(Decl(Type.BOOL, resolve.name)),
|
||||
self.checkedRead(None, ExprAddrOf(resolve), msgexpr,
|
||||
ExprAddrOf(itervar),
|
||||
errfn, "'%s'" % resolve.name,
|
||||
sentinelKey=resolve.name, errfnSentinel=errfnSent) ]
|
||||
desrej = [ StmtDecl(Decl(_PromiseRejectReason.Type(), reason.name)),
|
||||
self.checkedRead(None, ExprAddrOf(reason), msgexpr,
|
||||
ExprAddrOf(itervar),
|
||||
errfn, "'%s'" % reason.name,
|
||||
sentinelKey=reason.name, errfnSentinel=errfnSent),
|
||||
self.endRead(msgvar, itervar) ]
|
||||
prologue = ([
|
||||
self.logMessage(md, msgexpr, 'Received ',
|
||||
receiving=True),
|
||||
self.profilerLabel(md),
|
||||
Whitespace.NL
|
||||
])
|
||||
|
||||
def deserializeReply(self, md, replyexpr, side, errfn, errfnSentinel, actor=None):
|
||||
if not md.returns:
|
||||
return prologue
|
||||
|
||||
prologue.extend([ StmtDecl(Decl(_iterType(ptr=0), itervar.name),
|
||||
initargs=[ msgvar ]) ]
|
||||
+ desresolve)
|
||||
|
||||
start, decls, reads = 0, [], []
|
||||
if isctor:
|
||||
# return the raw actor handle so that its ID can be used
|
||||
# to construct the "real" actor
|
||||
handlevar = self.handlevar
|
||||
handletype = Type('ActorHandle')
|
||||
decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
|
||||
reads = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
|
||||
ExprAddrOf(itervar),
|
||||
errfn, "'%s'" % handletype.name,
|
||||
sentinelKey='actor', errfnSentinel=errfnSent) ]
|
||||
start = 1
|
||||
|
||||
stmts = (
|
||||
decls + [ StmtDecl(Decl(p.bareType(side), p.var().name))
|
||||
for p in md.returns ]
|
||||
+ [ Whitespace.NL ]
|
||||
+ reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
|
||||
msgexpr, ExprAddrOf(itervar),
|
||||
errfn, "'%s'" % p.bareType(side).name,
|
||||
sentinelKey=p.name, errfnSentinel=errfnSent)
|
||||
for p in md.returns[start:] ]
|
||||
+ [ self.endRead(msgvar, itervar) ])
|
||||
|
||||
return resolve, reason, prologue, desrej, stmts
|
||||
|
||||
def deserializeReply(self, md, replyexpr, side, errfn, errfnSentinel, actor=None, decls=False):
|
||||
stmts = [ Whitespace.NL,
|
||||
self.logMessage(md, replyexpr,
|
||||
'Received reply ', actor, receiving=True) ]
|
||||
@ -4234,10 +4475,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return stmts
|
||||
|
||||
itervar = self.itervar
|
||||
declstmts = []
|
||||
if decls:
|
||||
declstmts = [ StmtDecl(Decl(p.bareType(side), p.var().name))
|
||||
for p in md.returns ]
|
||||
stmts.extend(
|
||||
[ Whitespace.NL,
|
||||
StmtDecl(Decl(_iterType(ptr=0), itervar.name),
|
||||
initargs= [ self.replyvar ]) ]
|
||||
+ declstmts
|
||||
+ [ Whitespace.NL ]
|
||||
+ [ self.checkedRead(r.ipdltype, r.var(),
|
||||
ExprAddrOf(self.replyvar),
|
||||
ExprAddrOf(self.itervar),
|
||||
@ -4250,20 +4497,38 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
def sendAsync(self, md, msgexpr, actor=None):
|
||||
sendok = ExprVar('sendok__')
|
||||
return (
|
||||
sendok,
|
||||
([ Whitespace.NL,
|
||||
self.logMessage(md, msgexpr, 'Sending ', actor),
|
||||
self.profilerLabel(md) ]
|
||||
+ self.transition(md, actor)
|
||||
+ [ Whitespace.NL,
|
||||
StmtDecl(Decl(Type.BOOL, sendok.name),
|
||||
init=ExprCall(
|
||||
ExprSelect(self.protocol.callGetChannel(actor),
|
||||
'->', 'Send'),
|
||||
args=[ msgexpr ]))
|
||||
])
|
||||
)
|
||||
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__') ])) ])
|
||||
sendargs = [ msgexpr ]
|
||||
stmts = [ Whitespace.NL,
|
||||
self.logMessage(md, msgexpr, 'Sending ', actor),
|
||||
self.profilerLabel(md) ] + self.transition(md, actor)
|
||||
|
||||
if md.returns:
|
||||
sendargs.append(ExprCall(ExprSelect(retpromise, '.', 'get')));
|
||||
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)
|
||||
|
||||
return (retvar, stmts)
|
||||
|
||||
def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
|
||||
sendok = ExprVar('sendok__')
|
||||
@ -4317,9 +4582,12 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return ExprCall(ExprSelect(actorexpr, '->', 'DeallocSubtree'))
|
||||
|
||||
def invokeRecvHandler(self, md, implicit=1):
|
||||
retsems = 'in'
|
||||
if md.decl.type.isAsync() and md.returns:
|
||||
retsems = 'resolver'
|
||||
failif = StmtIf(ExprNot(
|
||||
ExprCall(md.recvMethod(),
|
||||
args=md.makeCxxArgs(paramsems='move', retsems='in',
|
||||
args=md.makeCxxArgs(paramsems='move', retsems=retsems,
|
||||
retcallsems='out',
|
||||
implicit=implicit))))
|
||||
failif.addifstmts([
|
||||
@ -4336,12 +4604,18 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
def makeSendMethodDecl(self, md):
|
||||
implicit = md.decl.type.hasImplicitActorParam()
|
||||
if md.decl.type.isAsync() and md.returns:
|
||||
returnsems = 'promise'
|
||||
rettype = _refptr(_makePromise(md.returns, self.side))
|
||||
else:
|
||||
returnsems = 'out'
|
||||
rettype = Type.BOOL
|
||||
decl = MethodDecl(
|
||||
md.sendMethod().name,
|
||||
params=md.makeCxxParams(paramsems='in', returnsems='out',
|
||||
params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
|
||||
side=self.side, implicit=implicit),
|
||||
warn_unused=(self.side == 'parent'),
|
||||
ret=Type.BOOL)
|
||||
ret=rettype)
|
||||
if md.decl.type.isCtor():
|
||||
decl.ret = md.actorDecl().bareType(self.side)
|
||||
return decl
|
||||
|
@ -209,6 +209,8 @@ class MessageType(IPDLType):
|
||||
def isOut(self): return self.direction is OUT
|
||||
def isInout(self): return self.direction is INOUT
|
||||
|
||||
def hasReply(self): return len(self.returns) or IPDLType.hasReply(self)
|
||||
|
||||
def hasImplicitActorParam(self):
|
||||
return self.isCtor() or self.isDtor()
|
||||
|
||||
@ -1119,11 +1121,10 @@ class CheckTypes(TcheckVisitor):
|
||||
"message `%s' requires more powerful send semantics than its protocol `%s' provides",
|
||||
mname, pname)
|
||||
|
||||
if mtype.isAsync() and len(mtype.returns):
|
||||
# XXX/cjones could modify grammar to disallow this ...
|
||||
if (mtype.isCtor() or mtype.isDtor()) and mtype.isAsync() and mtype.returns:
|
||||
self.error(loc,
|
||||
"asynchronous message `%s' declares return values",
|
||||
mname)
|
||||
"asynchronous ctor/dtor message `%s' declares return values",
|
||||
mname);
|
||||
|
||||
if (mtype.compress and
|
||||
(not mtype.isAsync() or mtype.isCtor() or mtype.isDtor())):
|
||||
|
17
ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl
Normal file
17
ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl
Normal file
@ -0,0 +1,17 @@
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
protocol PTestAsyncReturns {
|
||||
|
||||
child:
|
||||
async Ping() returns (bool one);
|
||||
async NoReturn() returns (bool unused);
|
||||
|
||||
parent:
|
||||
async Pong() returns (uint32_t param1, uint32_t param2);
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
109
ipc/ipdl/test/cxx/TestAsyncReturns.cpp
Normal file
109
ipc/ipdl/test/cxx/TestAsyncReturns.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include "TestAsyncReturns.h"
|
||||
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
static uint32_t sMagic1 = 0x105b59fb;
|
||||
static uint32_t sMagic2 = 0x09b6f5e3;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parent
|
||||
|
||||
TestAsyncReturnsParent::TestAsyncReturnsParent()
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestAsyncReturnsParent);
|
||||
}
|
||||
|
||||
TestAsyncReturnsParent::~TestAsyncReturnsParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestAsyncReturnsParent);
|
||||
}
|
||||
|
||||
void
|
||||
TestAsyncReturnsParent::Main()
|
||||
{
|
||||
if (!AbstractThread::GetCurrent()) {
|
||||
fail("AbstractThread not initalized");
|
||||
}
|
||||
SendNoReturn()->Then(AbstractThread::GetCurrent(), __func__,
|
||||
[](bool unused) {
|
||||
fail("resolve handler should not be called");
|
||||
},
|
||||
[](PromiseRejectReason aReason) {
|
||||
// MozPromise asserts in debug build if the
|
||||
// handler is not called
|
||||
if (aReason != PromiseRejectReason::ChannelClosed) {
|
||||
fail("reject with wrong reason");
|
||||
}
|
||||
passed("reject handler called on channel close");
|
||||
});
|
||||
SendPing()->Then(AbstractThread::GetCurrent(), __func__,
|
||||
[this](bool one) {
|
||||
if (one) {
|
||||
passed("take one argument");
|
||||
} else {
|
||||
fail("get one argument but has wrong value");
|
||||
}
|
||||
Close();
|
||||
},
|
||||
[](PromiseRejectReason aReason) {
|
||||
fail("sending Ping");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestAsyncReturnsParent::RecvPong(RefPtr<PongPromise>&& aPromise)
|
||||
{
|
||||
aPromise->Resolve(MakeTuple(sMagic1, sMagic2), __func__);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// child
|
||||
|
||||
TestAsyncReturnsChild::TestAsyncReturnsChild()
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestAsyncReturnsChild);
|
||||
}
|
||||
|
||||
TestAsyncReturnsChild::~TestAsyncReturnsChild()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestAsyncReturnsChild);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestAsyncReturnsChild::RecvNoReturn(RefPtr<NoReturnPromise>&& aPromise)
|
||||
{
|
||||
// Leak the promise intentionally
|
||||
aPromise->AddRef();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestAsyncReturnsChild::RecvPing(RefPtr<PingPromise>&& aPromise)
|
||||
{
|
||||
if (!AbstractThread::GetCurrent()) {
|
||||
fail("AbstractThread not initalized");
|
||||
}
|
||||
SendPong()->Then(AbstractThread::GetCurrent(), __func__,
|
||||
[aPromise](const Tuple<uint32_t, uint32_t>& aParam) {
|
||||
if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) {
|
||||
passed("take two arguments");
|
||||
} else {
|
||||
fail("get two argument but has wrong value");
|
||||
}
|
||||
aPromise->Resolve(true, __func__);
|
||||
},
|
||||
[](PromiseRejectReason aReason) {
|
||||
fail("sending Pong");
|
||||
});
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
62
ipc/ipdl/test/cxx/TestAsyncReturns.h
Normal file
62
ipc/ipdl/test/cxx/TestAsyncReturns.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef mozilla__ipdltest_TestAsyncReturns_h
|
||||
#define mozilla__ipdltest_TestAsyncReturns_h 1
|
||||
|
||||
#include "mozilla/_ipdltest/IPDLUnitTests.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestAsyncReturnsParent.h"
|
||||
#include "mozilla/_ipdltest/PTestAsyncReturnsChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
class TestAsyncReturnsParent :
|
||||
public PTestAsyncReturnsParent
|
||||
{
|
||||
public:
|
||||
TestAsyncReturnsParent();
|
||||
virtual ~TestAsyncReturnsParent();
|
||||
|
||||
static bool RunTestInProcesses() { return true; }
|
||||
static bool RunTestInThreads() { return true; }
|
||||
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
mozilla::ipc::IPCResult RecvPong(RefPtr<PongPromise>&& aPromise) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
passed("ok");
|
||||
QuitParent();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TestAsyncReturnsChild :
|
||||
public PTestAsyncReturnsChild
|
||||
{
|
||||
public:
|
||||
TestAsyncReturnsChild();
|
||||
virtual ~TestAsyncReturnsChild();
|
||||
|
||||
protected:
|
||||
mozilla::ipc::IPCResult RecvPing(RefPtr<PingPromise>&& aPromise) override;
|
||||
mozilla::ipc::IPCResult RecvNoReturn(RefPtr<NoReturnPromise>&& aPromise) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
QuitChild();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // ifndef mozilla__ipdltest_TestAsyncReturns_h
|
@ -15,6 +15,7 @@ EXPORTS.mozilla._ipdltest += [
|
||||
|
||||
SOURCES += [
|
||||
'TestActorPunning.cpp',
|
||||
'TestAsyncReturns.cpp',
|
||||
'TestBadActor.cpp',
|
||||
'TestCancel.cpp',
|
||||
'TestCrashCleanup.cpp',
|
||||
@ -62,6 +63,7 @@ IPDL_SOURCES += [
|
||||
'PTestActorPunning.ipdl',
|
||||
'PTestActorPunningPunned.ipdl',
|
||||
'PTestActorPunningSub.ipdl',
|
||||
'PTestAsyncReturns.ipdl',
|
||||
'PTestBadActor.ipdl',
|
||||
'PTestBadActorSub.ipdl',
|
||||
'PTestCancel.ipdl',
|
||||
|
8
ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl
Normal file
8
ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl
Normal file
@ -0,0 +1,8 @@
|
||||
include protocol AsyncCtorReturnsManagee;
|
||||
|
||||
protocol AsyncCtorReturns {
|
||||
manages AsyncCtorReturnsManagee;
|
||||
|
||||
child:
|
||||
async AsyncCtorReturnsManagee() returns (bool unused);
|
||||
};
|
8
ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl
Normal file
8
ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl
Normal file
@ -0,0 +1,8 @@
|
||||
include protocol AsyncCtorReturns;
|
||||
|
||||
protocol AsyncCtorReturnsManagee {
|
||||
manager AsyncCtorReturns;
|
||||
|
||||
parent:
|
||||
async __delete__();
|
||||
};
|
@ -779,6 +779,8 @@ XRE_InitParentProcess(int aArgc,
|
||||
// Set main thread before we initialize the profiler
|
||||
NS_SetMainThread();
|
||||
|
||||
mozilla::LogModule::Init();
|
||||
|
||||
char aLocal;
|
||||
GeckoProfilerInitRAII profiler(&aLocal);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user