author | Kan-Ru Chen <kanru@kanru.info> |
Thu, 16 Mar 2017 17:36:15 +0800 | |
changeset 565257 | 5ba21c7ff7632ab30422610cc782cd21e6908794 |
parent 565256 | 7f96694bda9b5da56995c03edb60c7dd9163ee0f |
child 624949 | 7d5aa6491bc23061a2265be822d726ade71ca387 |
push id | 54821 |
push user | bmo:kchen@mozilla.com |
push date | Wed, 19 Apr 2017 17:23:22 +0000 |
reviewers | billm, jwwang |
bugs | 1313200 |
milestone | 55.0a1 |
--- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -1,27 +1,28 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: sw=4 ts=4 et : */ /* 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/. */ #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" #include "nsDebug.h" #include "nsISupportsImpl.h" #include "nsContentUtils.h" #include <math.h> @@ -483,16 +484,37 @@ private: // Next item in mChan->mTransactionStack. AutoEnterTransaction *mNext; // Pointer the a reply received for this message, if one was received. 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), mListener(aListener), mChannelState(ChannelClosed), mSide(UnknownSide), mLink(nullptr), mWorkerLoop(nullptr), @@ -525,16 +547,21 @@ MessageChannel::MessageChannel(const cha mOnChannelConnectedTask = NewNonOwningCancelableRunnableMethod(this, &MessageChannel::DispatchOnChannelConnected); #ifdef OS_WIN 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() { MOZ_COUNT_DTOR(ipc::MessageChannel); IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors"); #ifdef OS_WIN if (mEvent) { @@ -667,16 +694,22 @@ MessageChannel::Clear() if (gParentProcessBlocker == this) { gParentProcessBlocker = nullptr; } if (mWorkerLoop) { mWorkerLoop->RemoveDestructionObserver(this); } + gUnresolvedPromises -= mPendingPromises.size(); + for (auto& pair : mPendingPromises) { + pair.second.mRejectFunction(__func__); + } + mPendingPromises.clear(); + mWorkerLoop = nullptr; delete mLink; mLink = nullptr; mOnChannelConnectedTask->Cancel(); if (mChannelErrorTask) { mChannelErrorTask->Cancel(); @@ -698,19 +731,23 @@ MessageChannel::Clear() bool MessageChannel::Open(Transport* aTransport, MessageLoop* aIOLoop, Side aSide) { NS_PRECONDITION(!mLink, "Open() called > once"); 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; return true; } bool MessageChannel::Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide) @@ -778,16 +815,21 @@ MessageChannel::OnOpenAsSlave(MessageCha } void 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; } bool MessageChannel::Echo(Message* aMsg) { nsAutoPtr<Message> msg(aMsg); @@ -846,16 +888,29 @@ MessageChannel::Send(Message* aMsg) if (!Connected()) { ReportConnectionError("MessageChannel", msg); return false; } mLink->SendMessage(msg.forget()); 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: BuildIDMessage() : IPC::Message(MSG_ROUTING_NONE, BUILD_ID_MESSAGE_TYPE) { } void Log(const std::string& aPrefix, FILE* aOutf) const
--- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -6,35 +6,41 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef ipc_glue_MessageChannel_h #define ipc_glue_MessageChannel_h 1 #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" #endif // defined(OS_WIN) #include "mozilla/ipc/Transport.h" #if defined(MOZ_CRASHREPORTER) && defined(OS_WIN) #include "mozilla/mozalloc_oom.h" #include "nsExceptionHandler.h" #endif #include "MessageLink.h" #include <deque> #include <functional> +#include <map> +#include <math.h> #include <stack> -#include <math.h> namespace mozilla { +class AbstractThread; + namespace ipc { class MessageChannel; class IToplevelProtocol; class RefCountedMonitor : public Monitor { public: @@ -56,16 +62,23 @@ enum class SyncSendError { NotConnectedBeforeSend, DisconnectedDuringSend, CancelledBeforeSend, CancelledAfterSend, TimedOut, ReplyError, }; +enum class PromiseRejectReason { + SendError, + ChannelClosed, + HandlerRejected, + EndGuard_, +}; + enum ChannelState { ChannelClosed, ChannelOpening, ChannelConnected, ChannelTimeout, ChannelClosing, ChannelError }; @@ -77,16 +90,24 @@ class MessageChannel : HasResultCodes, M friend class ProcessLink; friend class ThreadLink; class CxxStackFrame; class InterruptFrame; 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; typedef IPC::Message Message; typedef IPC::MessageInfo MessageInfo; typedef mozilla::ipc::Transport Transport; explicit MessageChannel(const char *aName, @@ -149,16 +170,35 @@ class MessageChannel : HasResultCodes, M REQUIRE_A11Y_REENTRY = 1 << 1, }; void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; } ChannelFlags GetChannelFlags() { return mFlags; } // 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 // channel bool Echo(Message* aMsg); // Synchronously send |msg| (i.e., wait for |reply|) bool Send(Message* aMsg, Message* aReply); @@ -166,16 +206,19 @@ class MessageChannel : HasResultCodes, M // Make an Interrupt call to the other side of the channel bool Call(Message* aMsg, Message* aReply); // Wait until a message is received bool WaitForIncomingMessage(); 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 { AssertWorkerThread(); return mLastSendError; } // Currently only for debugging purposes, doesn't aquire mMonitor. @@ -485,47 +528,49 @@ class MessageChannel : HasResultCodes, M bool mScheduled : 1; }; bool ShouldRunMessage(const Message& aMsg); void RunMessage(MessageTask& aTask); 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; private: // This will be a string literal, so lifetime is not an issue. const char* mName; // Based on presumption the listener owns and overlives the channel, // this is never nullified. IToplevelProtocol* mListener; ChannelState mChannelState; RefPtr<RefCountedMonitor> mMonitor; 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 // during channel shutdown. int mWorkerLoopID; // Timeout periods are broken up in two to prevent system suspension from // triggering an abort. This method (called by WaitForEvent with a 'did // timeout' flag) decides if we should wait again for half of mTimeoutMs // or give up. int32_t mTimeoutMs; bool mInTimeoutSecondHalf; // Worker-thread only; sequence numbers for messages that require - // synchronous replies. + // replies. int32_t mNextSeqno; static bool sIsPumpingMessages; // If ::Send returns false, this gives a more descriptive error. SyncSendError mLastSendError; template<class T> @@ -684,16 +729,19 @@ class MessageChannel : HasResultCodes, M // mMonitor. bool mIsWaitingForIncoming; // Map of replies received "out of turn", because of Interrupt // in-calls racing with replies to outstanding in-calls. See // 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; #ifdef OS_WIN HANDLE mEvent; #endif @@ -717,9 +765,18 @@ class MessageChannel : HasResultCodes, M }; void 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
new file mode 100644 --- /dev/null +++ b/ipc/glue/MessageLoopAbstractThreadWrapper.h @@ -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
--- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -19,16 +19,17 @@ #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Attributes.h" #include "mozilla/ipc/FileDescriptor.h" #include "mozilla/ipc/Shmem.h" #include "mozilla/ipc/Transport.h" #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" #include "MainThreadUtils.h" #if defined(ANDROID) && defined(DEBUG) #include <android/log.h> #endif
--- a/ipc/ipdl/ipdl/cxx/ast.py +++ b/ipc/ipdl/ipdl/cxx/ast.py @@ -163,16 +163,19 @@ class Visitor: ed.obj.accept(self) def visitExprMemberInit(self, minit): self.visitExprCall(minit) def visitExprSizeof(self, es): self.visitExprCall(es) + def visitExprLambda(self, l): + self.visitBlock(l) + def visitStmtBlock(self, sb): self.visitBlock(sb) def visitStmtDecl(self, sd): sd.decl.accept(self) if sd.init is not None: sd.init.accept(self) @@ -287,18 +290,22 @@ class Namespace(Block): Block.__init__(self) self.name = name class Type(Node): def __init__(self, name, const=0, 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. ptr => T* ptrconst => T* const ptrptr => T** ptrconstptr => T* const* @@ -313,25 +320,27 @@ Any type, naked or pointer, can be const self.const = const self.ptr = ptr self.ptrconst = ptrconst self.ptrptr = ptrptr self.ptrconstptr = ptrconstptr 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): return Type(self.name, const=self.const, 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') Type.INTPTR = Type('intptr_t') Type.NSRESULT = Type('nsresult') Type.UINT32 = Type('uint32_t') Type.UINT32PTR = Type('uint32_t', ptr=1) Type.SIZE = Type('size_t') @@ -637,22 +646,25 @@ class ExprIndex(Node): Node.__init__(self) self.arr = arr self.idx = idx 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='='): Node.__init__(self) self.lhs = lhs self.op = op self.rhs = rhs @@ -688,16 +700,25 @@ class ExprDelete(Node): class ExprMemberInit(ExprCall): def __init__(self, member, args=[ ]): ExprCall.__init__(self, member, args) 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): def __init__(self, stmts=[ ]): Block.__init__(self) self.addstmts(stmts) class StmtDecl(Node):
--- a/ipc/ipdl/ipdl/cxx/cgen.py +++ b/ipc/ipdl/ipdl/cxx/cgen.py @@ -33,19 +33,29 @@ class CxxCodeGen(CodePrinter, Visitor): def visitType(self, t): if t.const: self.write('const ') self.write(t.name) 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' elif t.ptrptr: ts += '**' elif t.ptrconstptr: ts += '* const*' ts += '&' * t.ref @@ -340,17 +350,18 @@ class CxxCodeGen(CodePrinter, Visitor): self.write('[') ei.idx.accept(self) self.write(']') def visitExprSelect(self, es): 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) self.write(' '+ ea.op +' ') ea.rhs.accept(self) def visitExprCall(self, ec): ec.func.accept(self) @@ -372,16 +383,34 @@ class CxxCodeGen(CodePrinter, Visitor): self.write('(') self.writeExprList(en.args) self.write(')') def visitExprDelete(self, ed): 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('{') self.indent() self.visitBlock(b) self.dedent() self.printdentln('}')
--- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -303,34 +303,50 @@ function would return true for |Actor[]| or (ipdltype.isArray() and _hasVisibleActor(ipdltype.basetype)))) def _abortIfFalse(cond, msg): return StmtExpr(ExprCall( 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')) def _refptrForget(expr): return ExprCall(ExprSelect(expr, '.', 'forget')) def _refptrTake(expr): return ExprCall(ExprSelect(expr, '.', 'take')) def _uniqueptr(T): return Type('UniquePtr', T=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) def _cxxManagedContainerType(basetype, const=0, ref=0): return Type('ManagedContainer', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False) def _callCxxArrayLength(arr): @@ -485,16 +501,25 @@ class _DestroyReason: def Type(): return Type('ActorDestroyReason') Deletion = ExprVar('Deletion') AncestorDeletion = ExprVar('AncestorDeletion') NormalShutdown = ExprVar('NormalShutdown') 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 class _ConvertToCxxType(TypeVisitor): def __init__(self, side, fq): self.side = side self.fq = fq @@ -909,16 +934,20 @@ class MessageDecl(ipdl.ast.MessageDecl): name += 'Constructor' return ExprVar(name) def hasReply(self): return (self.decl.type.hasReply() 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) def prettyMsgName(self, pfx=''): return pfx + self.msgCtorFunc() def pqMsgCtorFunc(self): return '%s::%s'% (self.namespace, self.msgCtorFunc()) @@ -935,16 +964,23 @@ class MessageDecl(ipdl.ast.MessageDecl): def replyId(self): return self.replyCtorFunc()+ '__ID' def pqReplyId(self): return '%s::%s'% (self.namespace, self.replyId()) 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] def makeCxxParams(self, paramsems='in', returnsems='out', side=None, implicit=1): """Return a list of C++ decls per the spec'd configuration. |params| and |returns| is the C++ semantics of those: 'in', 'out', or None.""" @@ -952,21 +988,29 @@ class MessageDecl(ipdl.ast.MessageDecl): if sems is 'in': return Decl(d.inType(side), d.name) elif sems is 'move': return Decl(d.moveType(side), d.name) elif sems is 'out': 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(): cxxparams = cxxparams[1:] return cxxparams def makeCxxArgs(self, paramsems='in', retsems='out', retcallsems='out', @@ -989,16 +1033,20 @@ class MessageDecl(ipdl.ast.MessageDecl): cxxargs.append(ExprAddrOf(ret.var())) else: assert 0 elif retsems is 'out': if retcallsems is 'in': cxxargs.append(ExprDeref(ret.var())) 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() cxxargs = cxxargs[1:] return cxxargs @@ -1247,17 +1295,19 @@ with some new IPDL/C++ nodes that are tu 'ProcessId'), Typedef(Type('mozilla::ipc::ProtocolId'), 'ProtocolId'), Typedef(Type('mozilla::ipc::Transport'), 'Transport'), 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): if tu not in self.visitedTus: self.visitedTus.add(tu) ipdl.ast.Visitor.visitTranslationUnit(self, tu) if not isinstance(tu, TranslationUnit): TranslationUnit.upgrade(tu) @@ -2561,31 +2611,37 @@ class _GenerateProtocolActorCode(ipdl.as self.prettyside)), Whitespace.NL ]) self.cls.addstmt(Label.PROTECTED) for typedef in p.cxxTypedefs(): 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) self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ]) # interface methods that the concrete subclass has to impl for md in p.messageDecls: isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor() 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) if isctor or isdtor: defaultRecv = MethodDefn(recvDecl) defaultRecv.addstmt(StmtReturn(ExprCall(ExprVar('IPC_OK')))) self.cls.addstmt(defaultRecv) else: @@ -3722,17 +3778,17 @@ class _GenerateProtocolActorCode(ipdl.as sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md) elif isctor: sendmethod = self.genBlockingCtorMethod(md) elif isdtor and isasync: sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md) elif isdtor: sendmethod = self.genBlockingDtorMethod(md) elif isasync: - sendmethod = self.genAsyncSendMethod(md) + sendmethod, (recvlbl, recvcase) = self.genAsyncSendMethod(md) else: sendmethod = self.genBlockingSendMethod(md) # XXX figure out what to do here if isdtor and md.decl.type.constructedType().isToplevel(): sendmethod = None if sendmethod is not None: @@ -3944,27 +4000,69 @@ class _GenerateProtocolActorCode(ipdl.as ]) def dtorPrologue(self, actorexpr): return [ self.failIfNullActor(actorexpr), Whitespace.NL ] 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): method = MethodDefn(self.makeSendMethodDecl(md)) msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor) replyvar = self.replyvar @@ -4053,22 +4151,25 @@ class _GenerateProtocolActorCode(ipdl.as def genRecvCase(self, md): lbl = CaseLabel(md.pqMsgId()) case = StmtBlock() stmts = self.deserializeMessage(md, self.side, errfn=errfnRecv, 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) + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv, self.replyvar) + [ StmtReturn(_Result.Processed) ]) return lbl, case @@ -4099,22 +4200,100 @@ class _GenerateProtocolActorCode(ipdl.as + [ Whitespace.NL ] + [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=this) for p in md.params ] + [ Whitespace.NL ] + self.setMessageFlags(md, msgvar, reply=0)) 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 ( [ StmtExpr(ExprAssn( replyvar, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))), Whitespace.NL ] + [ self.checkedWrite(r.ipdltype, r.var(), replyvar, sentinelKey=r.name) for r in md.returns ] @@ -4156,17 +4335,17 @@ class _GenerateProtocolActorCode(ipdl.as for p in params ] + [ self.endRead(msgvar, itervar) ] # Move the message back to its source before sending. + [ StmtExpr(ExprAssn(ExprDeref(msgsrcVar), ExprMove(msgvar))) ] )) return stmts - def setMessageFlags(self, md, var, reply): + def setMessageFlags(self, md, var, reply, seqno=None): stmts = [ ] if md.decl.type.isSync(): stmts.append(StmtExpr(ExprCall( ExprSelect(var, '->', 'set_sync')))) elif md.decl.type.isInterrupt(): stmts.append(StmtExpr(ExprCall( ExprSelect(var, '->', 'set_interrupt')))) @@ -4174,16 +4353,21 @@ class _GenerateProtocolActorCode(ipdl.as if md.decl.type.isCtor(): stmts.append(StmtExpr(ExprCall( ExprSelect(var, '->', 'set_constructor')))) if reply: stmts.append(StmtExpr(ExprCall( ExprSelect(var, '->', 'set_reply')))) + if seqno: + stmts.append(StmtExpr(ExprCall( + ExprSelect(var, '->', 'set_seqno'), + args=[ seqno ]))) + return stmts + [ Whitespace.NL ] def deserializeMessage(self, md, side, errfn, errfnSent): msgvar = self.msgvar itervar = self.itervar msgexpr = ExprAddrOf(msgvar) isctor = md.decl.type.isCtor() @@ -4220,55 +4404,136 @@ class _GenerateProtocolActorCode(ipdl.as msgexpr, ExprAddrOf(itervar), errfn, "'%s'" % p.bareType(side).name, sentinelKey=p.name, errfnSentinel=errfnSent) for p in md.params[start:] ] + [ self.endRead(msgvar, itervar) ])) return stmts - - def deserializeReply(self, md, replyexpr, side, errfn, errfnSentinel, actor=None): + 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 + ]) + + 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) ] if 0 == len(md.returns): 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), errfn, "'%s'" % r.bareType(side).name, sentinelKey=r.name, errfnSentinel=errfnSentinel) for r in md.returns ] + [ self.endRead(self.replyvar, itervar) ]) return stmts 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__') return ( sendok, ([ Whitespace.NL, self.logMessage(md, msgexpr, 'Sending ', actor), self.profilerLabel(md) ] @@ -4312,41 +4577,50 @@ class _GenerateProtocolActorCode(ipdl.as return ExprCall(removefunc, args=[ _protocolId(ipdltype), actorexpr ]) def callDeallocSubtree(self, md, actorexpr): 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([ _protocolErrorBreakpoint('Handler returned error code!'), Whitespace('// Error handled in mozilla::ipc::IPCResult\n', indent=1), StmtReturn(_Result.ProcessingError) ]) return [ failif ] def makeDtorMethodDecl(self, md): decl = self.makeSendMethodDecl(md) decl.static = 1 return decl 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 def logMessage(self, md, msgptr, pfx, actor=None, receiving=False): actorname = _actorName(self.protocol.name, self.side) return _ifLogging(ExprLiteral.String(actorname),
--- a/ipc/ipdl/ipdl/type.py +++ b/ipc/ipdl/ipdl/type.py @@ -204,16 +204,18 @@ class MessageType(IPDLType): def isCtor(self): return self.ctor def isDtor(self): return self.dtor def constructedType(self): return self.cdtype def isIn(self): return self.direction is IN 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() class ProtocolType(IPDLType): def __init__(self, qname, nested, sendSemantics): self.qname = qname self.nestedRange = (NOT_NESTED, nested) self.sendSemantics = sendSemantics @@ -1114,21 +1116,20 @@ class CheckTypes(TcheckVisitor): mname, pname) if mtype.needsMoreJuiceThan(ptype): self.error( loc, "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())): if mtype.isCtor() or mtype.isDtor(): message_type = "constructor" if mtype.isCtor() else "destructor" error_message = ("%s messages can't use compression (here, in protocol `%s')" % (message_type, pname))
new file mode 100644 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl @@ -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
new file mode 100644 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp @@ -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
new file mode 100644 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestAsyncReturns.h @@ -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
--- a/ipc/ipdl/test/cxx/moz.build +++ b/ipc/ipdl/test/cxx/moz.build @@ -10,16 +10,17 @@ EXPORTS.mozilla._ipdltest += [ 'IPDLUnitTestProcessChild.h', 'IPDLUnitTests.h', 'IPDLUnitTestTypes.h', 'IPDLUnitTestUtils.h', ] SOURCES += [ 'TestActorPunning.cpp', + 'TestAsyncReturns.cpp', 'TestBadActor.cpp', 'TestCancel.cpp', 'TestCrashCleanup.cpp', 'TestDataStructures.cpp', 'TestDemon.cpp', 'TestDesc.cpp', 'TestEndpointBridgeMain.cpp', 'TestEndpointOpens.cpp', @@ -57,16 +58,17 @@ SOURCES += [ 'IPDLUnitTestProcessChild.cpp', 'IPDLUnitTestSubprocess.cpp', ] IPDL_SOURCES += [ 'PTestActorPunning.ipdl', 'PTestActorPunningPunned.ipdl', 'PTestActorPunningSub.ipdl', + 'PTestAsyncReturns.ipdl', 'PTestBadActor.ipdl', 'PTestBadActorSub.ipdl', 'PTestCancel.ipdl', 'PTestCrashCleanup.ipdl', 'PTestDataStructures.ipdl', 'PTestDataStructuresCommon.ipdlh', 'PTestDataStructuresSub.ipdl', 'PTestDemon.ipdl',
new file mode 100644 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl @@ -0,0 +1,8 @@ +include protocol AsyncCtorReturnsManagee; + +protocol AsyncCtorReturns { + manages AsyncCtorReturnsManagee; + +child: + async AsyncCtorReturnsManagee() returns (bool unused); +};
new file mode 100644 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl @@ -0,0 +1,8 @@ +include protocol AsyncCtorReturns; + +protocol AsyncCtorReturnsManagee { + manager AsyncCtorReturns; + +parent: + async __delete__(); +};
rename from ipc/ipdl/test/ipdl/error/AsyncReturn.ipdl rename to ipc/ipdl/test/ipdl/ok/AsyncReturn.ipdl
--- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -774,16 +774,18 @@ XRE_InitParentProcess(int aArgc, { NS_ENSURE_ARG_MIN(aArgc, 1); NS_ENSURE_ARG_POINTER(aArgv); NS_ENSURE_ARG_POINTER(aArgv[0]); // Set main thread before we initialize the profiler NS_SetMainThread(); + mozilla::LogModule::Init(); + char aLocal; GeckoProfilerInitRAII profiler(&aLocal); ScopedXREEmbed embed; gArgc = aArgc; gArgv = aArgv; nsresult rv = XRE_InitCommandLine(gArgc, gArgv);