Bug 1368031 - Pass resolver function to Recv methods instead of a MozPromise r=billm
authorKan-Ru Chen <kanru@kanru.info>
Thu, 25 May 2017 21:25:52 -0400
changeset 361791 9a39e2222a478bb59bfbc1ee0d6d41adca1e392c
parent 361790 4507e6e48d484390f59047e6d9ecfba6662457c7
child 361792 e6f613b7ec08ca91240e1c28e687bad0ff38f2e4
push id31943
push userryanvm@gmail.com
push dateThu, 01 Jun 2017 15:54:45 +0000
treeherdermozilla-central@62005e6aecdf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1368031
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1368031 - Pass resolver function to Recv methods instead of a MozPromise r=billm Added new typedef for the Resolve function. The Resolve function is a std::function accepting corresponding parameters. When it's invoked it will send the reply immediately. Users can still wrap it in a MozPromise if they want promise chaining. The original typedef of the Promise type is repurposed as the Send* functions' return type. MozReview-Commit-ID: JZ4IsgJ87M1
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/cxx/cgen.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/test/cxx/TestAsyncReturns.cpp
ipc/ipdl/test/cxx/TestAsyncReturns.h
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -31,16 +31,19 @@ class Visitor:
 
     def visitTypeArray(self, ta):
         ta.basetype.accept(self)
         ta.nmemb.accept(self)
 
     def visitTypeEnum(self, enum):
         pass
 
+    def visitTypeFunction(self, fn):
+        pass
+
     def visitTypeUnion(self, union):
         for t, name in union.components:
             t.accept(self)
 
     def visitTypedef(self, tdef):
         tdef.fromtype.accept(self)
 
     def visitUsing(self, us):
@@ -370,16 +373,22 @@ class TypeUnion(Node):
     def __init__(self, name=None):
         Node.__init__(self)
         self.name = name
         self.components = [ ]           # [ Decl ]
 
     def addComponent(self, type, name):
         self.components.append(Decl(type, name))
 
+class TypeFunction(Node):
+    def __init__(self, params=[ ], ret=Type('void')):
+        '''Anonymous function type std::function<>'''
+        self.params = params
+        self.ret = ret
+
 class Typedef(Node):
     def __init__(self, fromtype, totypename, templateargs=[]):
         assert isinstance(totypename, str)
         
         Node.__init__(self)
         self.fromtype = fromtype
         self.totypename = totypename
         self.templateargs = templateargs
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -89,16 +89,22 @@ class CxxCodeGen(CodePrinter, Visitor):
         for decl in u.components:
             self.printdent()
             decl.accept(self)
             self.println(';')
         self.dedent()
 
         self.printdent('}')
 
+    def visitTypeFunction(self, fn):
+        self.write('std::function<')
+        fn.ret.accept(self)
+        self.write('(')
+        self.writeDeclList(fn.params)
+        self.write(')>')
 
     def visitTypedef(self, td):
         if td.templateargs:
             formals = ', '.join([ 'class ' + T for T in td.templateargs ])
             args = ', '.join(td.templateargs)
             self.printdent('template<' + formals + '> using ' + td.totypename + ' = ')
             td.fromtype.accept(self)
             self.println('<' + args + '>;')
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -337,16 +337,23 @@ def _makePromise(returns, side, resolver
     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 _makeResolver(returns, side):
+    if len(returns) > 1:
+        resolvetype = _tuple([d.bareType(side) for d in returns])
+    else:
+        resolvetype = returns[0].bareType(side)
+    return TypeFunction([Decl(resolvetype, '')])
+
 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):
@@ -973,16 +980,19 @@ class MessageDecl(ipdl.ast.MessageDecl):
 
     def promiseName(self):
         name = self.baseName()
         if self.decl.type.isCtor():
             name += 'Constructor'
         name += 'Promise'
         return name
 
+    def resolverName(self):
+        return self.baseName() + 'Resolver'
+
     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."""
 
@@ -991,18 +1001,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 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')
+            return Decl(Type(self.resolverName(), ref=2), 'aResolve')
 
         cxxparams = [ ]
         if paramsems is not None:
             cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
 
         if returnsems is 'promise' and self.returns:
             pass
         elif returnsems is 'resolver' and self.returns:
@@ -1038,17 +1047,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 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')))
+            cxxargs.append(ExprMove(ExprVar('resolver')))
 
         if not implicit:
             assert self.decl.type.hasImplicitActorParam()
             cxxargs = cxxargs[1:]
 
         return cxxargs
 
 
@@ -2572,17 +2581,17 @@ class _GenerateProtocolActorCode(ipdl.as
             self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
 
         self.hdrfile.addthings([
             CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
             Whitespace.NL ])
 
         hasAsyncReturns = False
         for md in p.messageDecls:
-            if self.receivesMessage(md) and md.hasAsyncReturns():
+            if md.hasAsyncReturns():
                 hasAsyncReturns = True
                 break
 
         inherits = []
         if ptype.isToplevel():
             inherits.append(Inherit(p.openedProtocolInterfaceType(),
                                     viz='public'))
         else:
@@ -2637,23 +2646,28 @@ class _GenerateProtocolActorCode(ipdl.as
             self.cls.addstmt(typedef)
         for typedef in self.includedActorTypedefs:
             self.cls.addstmt(typedef)
 
         self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ])
 
-        self.cls.addstmt(Label.PUBLIC)
-        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)
+        if hasAsyncReturns:
+            self.cls.addstmt(Label.PUBLIC)
+            for md in p.messageDecls:
+                if self.sendsMessage(md) and md.hasAsyncReturns():
+                    self.cls.addstmt(
+                        Typedef(_makePromise(md.returns, self.side),
+                                md.promiseName()))
+                if self.receivesMessage(md) and md.hasAsyncReturns():
+                    self.cls.addstmt(
+                        Typedef(_makeResolver(md.returns, self.side),
+                                md.resolverName()))
+            self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmt(Label.PROTECTED)
         # 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
@@ -4191,17 +4205,17 @@ class _GenerateProtocolActorCode(ipdl.as
 
         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)
+            declstmts = self.makeResolver(md, errfnRecv, routingId=idvar)
         case.addstmts(
             stmts
             + self.transition(md)
             + saveIdStmts
             + declstmts
             + self.invokeRecvHandler(md)
             + [ Whitespace.NL ]
             + self.makeReply(md, errfnRecv, routingId=idvar)
@@ -4237,27 +4251,27 @@ 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):
+    def makeResolver(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())
+        resolvertype = Type(md.resolverName())
         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'),
@@ -4277,73 +4291,42 @@ class _GenerateProtocolActorCode(ipdl.as
         ifactorisdead.addifstmts([_printWarningMessage("Not resolving promise because actor is dead."),
                                   StmtReturn()])
         ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
                                                self.protocol.deadState()))
         ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving promise because actor is destroyed."),
                                   StmtReturn()])
         returnifactorisdead = [ ifactorisdead,
                                 ifactorisdestroyed ]
-        promisethen = ExprLambda([ExprVar.THIS, selfvar, routingId, seqno],
+        resolverfn = ExprLambda([ExprVar.THIS, selfvar, routingId, seqno],
                                  [resolvedecl])
-        promisethen.addstmts(returnifactorisdead
-                             + [ StmtDecl(Decl(Type.BOOL, resolve.name),
-                                        init=ExprLiteral.TRUE) ]
-                             + [ StmtDecl(Decl(p.bareType(self.side), p.var().name))
+        resolverfn.addstmts(returnifactorisdead
+                            + [ 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)
+                            + [ 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, selfvar, routingId, seqno],
-                                [Decl(_PromiseRejectReason.Type(), reason.name)])
-
-        # If-statement for silently cancelling the promise due to ActorDestroyed.
-        returnifactordestroyed = StmtIf(ExprBinary(reason, '==',
-                                                   _PromiseRejectReason.ActorDestroyed))
-        returnifactordestroyed.addifstmts([_printWarningMessage("Reject due to ActorDestroyed"),
-                                           StmtReturn()])
-
-        promiserej.addstmts(returnifactorisdead +
-                            [ returnifactordestroyed,
-                              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)
+        resolverfn.addstmts(sendmsg)
 
         makepromise = [ Whitespace.NL,
                         StmtDecl(Decl(Type.INT32, seqno.name),
                                  init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
                         StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
                                       selfvar.name),
                                  init=ExprVar.THIS),
-                        StmtDecl(Decl(_refptr(promise), 'promise'),
-                                 init=ExprNew(promise, args=[ExprVar('__func__')])),
-                        StmtExpr(ExprCall(
-                            ExprSelect(ExprVar('promise'), '->', 'Then'),
-                            args=[ ExprCall(ExprVar('AbstractThread::GetCurrent')),
-                                   ExprVar('__func__'),
-                                   promisethen,
-                                   promiserej ])) ]
+                        StmtDecl(Decl(resolvertype, 'resolver'),
+                                 init=resolverfn) ]
         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 [ ]
@@ -4662,17 +4645,17 @@ class _GenerateProtocolActorCode(ipdl.as
         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))
+            rettype = _refptr(Type(md.promiseName()))
         else:
             returnsems = 'out'
             rettype = Type.BOOL
         decl = MethodDecl(
             md.sendMethod().name,
             params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
                                     side=self.side, implicit=implicit),
             warn_unused=(self.side == 'parent'),
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
@@ -52,19 +52,19 @@ TestAsyncReturnsParent::Main()
                    },
                    [](PromiseRejectReason aReason) {
                      fail("sending Ping");
                    });
 }
 
 
 mozilla::ipc::IPCResult
-TestAsyncReturnsParent::RecvPong(RefPtr<PongPromise>&& aPromise)
+TestAsyncReturnsParent::RecvPong(PongResolver&& aResolve)
 {
-  aPromise->Resolve(MakeTuple(sMagic1, sMagic2), __func__);
+  aResolve(MakeTuple(sMagic1, sMagic2));
   return IPC_OK();
 }
 
 
 //-----------------------------------------------------------------------------
 // child
 
 TestAsyncReturnsChild::TestAsyncReturnsChild()
@@ -73,37 +73,36 @@ TestAsyncReturnsChild::TestAsyncReturnsC
 }
 
 TestAsyncReturnsChild::~TestAsyncReturnsChild()
 {
   MOZ_COUNT_DTOR(TestAsyncReturnsChild);
 }
 
 mozilla::ipc::IPCResult
-TestAsyncReturnsChild::RecvNoReturn(RefPtr<NoReturnPromise>&& aPromise)
+TestAsyncReturnsChild::RecvNoReturn(NoReturnResolver&& aResolve)
 {
-  // Leak the promise intentionally
-  Unused << do_AddRef(aPromise);
+  // Not resolving the promise intentionally
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-TestAsyncReturnsChild::RecvPing(RefPtr<PingPromise>&& aPromise)
+TestAsyncReturnsChild::RecvPing(PingResolver&& aResolve)
 {
   if (!AbstractThread::MainThread()) {
     fail("AbstractThread not initalized");
   }
   SendPong()->Then(AbstractThread::MainThread(), __func__,
-                   [aPromise](const Tuple<uint32_t, uint32_t>& aParam) {
+                   [aResolve](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__);
+                     aResolve(true);
                    },
                    [](PromiseRejectReason aReason) {
                      fail("sending Pong");
                    });
   return IPC_OK();
 }
 
 
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.h
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.h
@@ -18,17 +18,17 @@ public:
   virtual ~TestAsyncReturnsParent();
 
   static bool RunTestInProcesses() { return true; }
   static bool RunTestInThreads() { return true; }
 
   void Main();
 
 protected:
-  mozilla::ipc::IPCResult RecvPong(RefPtr<PongPromise>&& aPromise) override;
+  mozilla::ipc::IPCResult RecvPong(PongResolver&& aResolve) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override
   {
     if (NormalShutdown != why)
       fail("unexpected destruction!");
     passed("ok");
     QuitParent();
   }
@@ -38,18 +38,18 @@ protected:
 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;
+  mozilla::ipc::IPCResult RecvPing(PingResolver&& aResolve) override;
+  mozilla::ipc::IPCResult RecvNoReturn(NoReturnResolver&& aResolve) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override
   {
     if (NormalShutdown != why)
       fail("unexpected destruction!");
     QuitChild();
   }
 };