Bug 771765 - Support template content process, part 9: allocating a toplevel protocol should return itself. r=dzbarsky
authorCervantes Yu <cyu@mozilla.com>
Thu, 26 Sep 2013 12:19:09 +0800
changeset 163146 eb41ace1d0b568b26dea6df503bf8d5cc0c1819f
parent 163145 b0b107f7e678ad226ca91d43896523f0ed5d71d3
child 163147 9f0840f1eab036c083c0cdd2034e059fafb45909
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdzbarsky
bugs771765, 879475
milestone27.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 771765 - Support template content process, part 9: allocating a toplevel protocol should return itself. r=dzbarsky Reverse the effect in bug 879475 part 14 since we need to keep track of open protocols in the Nuwa process.
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
gfx/layers/ipc/CompositorChild.cpp
gfx/layers/ipc/CompositorChild.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/ImageBridgeParent.cpp
gfx/layers/ipc/ImageBridgeParent.h
ipc/ipdl/ipdl/lower.py
ipc/ipdl/test/cxx/TestBridgeMain.cpp
ipc/ipdl/test/cxx/TestBridgeMain.h
ipc/ipdl/test/cxx/TestOpens.cpp
ipc/ipdl/test/cxx/TestOpens.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -526,24 +526,24 @@ ContentChild::RecvDumpGCAndCCLogsToFile(
 {
     nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
 
     dumper->DumpGCAndCCLogsToFile(aIdentifier, aDumpAllTraces,
                                   aDumpChildProcesses);
     return true;
 }
 
-bool
+PCompositorChild*
 ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess)
 {
     return CompositorChild::Create(aTransport, aOtherProcess);
 }
 
-bool
+PImageBridgeChild*
 ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                                      base::ProcessId aOtherProcess)
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
 bool
 ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -72,20 +72,20 @@ public:
 
     const AppInfo& GetAppInfo() {
         return mAppInfo;
     }
 
     void SetProcessName(const nsAString& aName);
     const void GetProcessName(nsAString& aName);
 
-    bool
+    PCompositorChild*
     AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) MOZ_OVERRIDE;
-    bool
+    PImageBridgeChild*
     AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual bool RecvSetProcessPrivileges(const ChildPrivileges& aPrivs);
 
     virtual PBrowserChild* AllocPBrowserChild(const IPCTabContext &aContext,
                                               const uint32_t &chromeFlags);
     virtual bool DeallocPBrowserChild(PBrowserChild*);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2090,24 +2090,24 @@ ContentParent::Observe(nsISupports* aSub
              !strcmp(aTopic, "a11y-init-or-shutdown")) {
         unused << SendActivateA11y();
     }
 #endif
 
     return NS_OK;
 }
 
-bool
+PCompositorParent*
 ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                                       base::ProcessId aOtherProcess)
 {
     return CompositorParent::Create(aTransport, aOtherProcess);
 }
 
-bool
+PImageBridgeParent*
 ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
     return ImageBridgeParent::Create(aTransport, aOtherProcess);
 }
 
 bool
 ContentParent::RecvGetProcessAttributes(uint64_t* aId,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -299,20 +299,20 @@ private:
      *
      * If aCloseWithError is true and this is the first call to
      * ShutDownProcess, then we'll close our channel using CloseWithError()
      * rather than vanilla Close().  CloseWithError() indicates to IPC that this
      * is an abnormal shutdown (e.g. a crash).
      */
     void ShutDownProcess(bool aCloseWithError);
 
-    bool
+    PCompositorParent*
     AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
-    bool
+    PImageBridgeParent*
     AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual bool RecvGetProcessAttributes(uint64_t* aId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) MOZ_OVERRIDE;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
 
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -46,36 +46,35 @@ CompositorChild::Destroy()
   while (size_t len = ManagedPLayerTransactionChild().Length()) {
     LayerTransactionChild* layers =
       static_cast<LayerTransactionChild*>(ManagedPLayerTransactionChild()[len - 1]);
     layers->Destroy();
   }
   SendStop();
 }
 
-/*static*/ bool
+/*static*/ PCompositorChild*
 CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess)
 {
   // There's only one compositor per child process.
   MOZ_ASSERT(!sCompositor);
 
   nsRefPtr<CompositorChild> child(new CompositorChild(nullptr));
   ProcessHandle handle;
   if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
     // We can't go on without a compositor.
     NS_RUNTIMEABORT("Couldn't OpenProcessHandle() to parent process.");
-    return false;
+    return nullptr;
   }
   if (!child->Open(aTransport, handle, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
     NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
-    return false;
+    return nullptr;
   }
   // We release this ref in ActorDestroy().
-  sCompositor = child.forget().get();
-  return true;
+  return sCompositor = child.forget().get();
 }
 
 /*static*/ PCompositorChild*
 CompositorChild::Get()
 {
   // This is only expected to be used in child processes.
   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
   return sCompositor;
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -33,17 +33,17 @@ public:
 
   void Destroy();
 
   /**
    * We're asked to create a new Compositor in response to an Opens()
    * or Bridge() request from our parent process.  The Transport is to
    * the compositor's context.
    */
-  static bool
+  static PCompositorChild*
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
   static PCompositorChild* Get();
 
   static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
 protected:
   virtual PLayerTransactionChild*
     AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -915,32 +915,34 @@ static void
 OpenCompositor(CrossProcessCompositorParent* aCompositor,
                Transport* aTransport, ProcessHandle aHandle,
                MessageLoop* aIOLoop)
 {
   DebugOnly<bool> ok = aCompositor->Open(aTransport, aHandle, aIOLoop);
   MOZ_ASSERT(ok);
 }
 
-/*static*/ bool
+/*static*/ PCompositorParent*
 CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess)
 {
   nsRefPtr<CrossProcessCompositorParent> cpcp =
     new CrossProcessCompositorParent(aTransport);
   ProcessHandle handle;
   if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
     // XXX need to kill |aOtherProcess|, it's boned
-    return false;
+    return nullptr;
   }
   cpcp->mSelfRef = cpcp;
   CompositorLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(OpenCompositor, cpcp.get(),
                         aTransport, handle, XRE_GetIOMessageLoop()));
-  return true;
+  // The return value is just compared to null for success checking,
+  // we're not sharing a ref.
+  return cpcp.get();
 }
 
 IToplevelProtocol*
 CompositorParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
                                 base::ProcessHandle aPeerProcess,
                                 mozilla::ipc::ProtocolCloneContext* aCtx)
 {
   for (unsigned int i = 0; i < aFds.Length(); i++) {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -177,17 +177,17 @@ public:
    * pan/zoom-related events can be sent.
    */
   static APZCTreeManager* GetAPZCTreeManager(uint64_t aLayersId);
 
   /**
    * A new child process has been configured to push transactions
    * directly to us.  Transport is to its thread context.
    */
-  static bool
+  static PCompositorParent*
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
   /**
    * Setup external message loop and thread ID for Compositor.
    * Should be used when CompositorParent should work in existing thread/MessageLoop,
    * for example moving Compositor into native toolkit main thread will allow to avoid
    * extra synchronization and call ::Composite() right from toolkit::Paint event
    */
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -520,30 +520,30 @@ ImageBridgeChild::EndTransaction()
     }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 }
 
 
-bool
+PImageBridgeChild*
 ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
                                         ProcessId aOtherProcess)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
 
   ProcessHandle processHandle;
   if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
-    return false;
+    return nullptr;
   }
 
   sImageBridgeChildThread = new Thread("ImageBridgeChild");
   if (!sImageBridgeChildThread->Start()) {
-    return false;
+    return nullptr;
   }
 
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
     sImageBridgeChildThread
       ->message_loop()->PostTask(FROM_HERE,
                                  NewRunnableFunction(NuwaMarkCurrentThread,
                                                      (void (*)(void *))nullptr,
@@ -552,17 +552,17 @@ ImageBridgeChild::StartUpInChildProcess(
 #endif
 
   sImageBridgeChildSingleton = new ImageBridgeChild();
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(ConnectImageBridgeInChildProcess,
                         aTransport, processHandle));
 
-  return true;
+  return sImageBridgeChildSingleton;
 }
 
 void ImageBridgeChild::ShutDown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
   if (ImageBridgeChild::IsCreated()) {
     ImageBridgeChild::DestroyBridge();
     delete sImageBridgeChildThread;
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -109,17 +109,17 @@ public:
   /**
    * Creates the image bridge with a dedicated thread for ImageBridgeChild.
    *
    * We may want to use a specifi thread in the future. In this case, use
    * CreateWithThread instead.
    */
   static void StartUp();
 
-  static bool
+  static PImageBridgeChild*
   StartUpInChildProcess(Transport* aTransport, ProcessId aOtherProcess);
 
   /**
    * Destroys the image bridge by calling DestroyBridge, and destroys the
    * ImageBridge's thread.
    *
    * If you don't want to destroy the thread, call DestroyBridge directly
    * instead.
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -97,31 +97,31 @@ ImageBridgeParent::RecvUpdateNoSwap(cons
 static void
 ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
                                   Transport* aTransport,
                                   ProcessHandle aOtherProcess)
 {
   aBridge->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), ipc::ParentSide);
 }
 
-/*static*/ bool
+/*static*/ PImageBridgeParent*
 ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
 {
   ProcessHandle processHandle;
   if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
-    return false;
+    return nullptr;
   }
 
   MessageLoop* loop = CompositorParent::CompositorLoop();
   nsRefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aTransport);
   bridge->mSelfRef = bridge;
   loop->PostTask(FROM_HERE,
                  NewRunnableFunction(ConnectImageBridgeInParentProcess,
                                      bridge.get(), aTransport, processHandle));
-  return true;
+  return bridge.get();
 }
 
 bool ImageBridgeParent::RecvStop()
 {
   return true;
 }
 
 static  uint64_t GenImageContainerID() {
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -39,17 +39,17 @@ public:
   typedef InfallibleTArray<CompositableOperation> EditArray;
   typedef InfallibleTArray<EditReply> EditReplyArray;
 
   ImageBridgeParent(MessageLoop* aLoop, Transport* aTransport);
   ~ImageBridgeParent();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
-  static bool
+  static PImageBridgeParent*
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
   virtual PGrallocBufferParent*
   AllocPGrallocBufferParent(const gfxIntSize&, const uint32_t&, const uint32_t&,
                             MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPGrallocBufferParent(PGrallocBufferParent* actor) MOZ_OVERRIDE;
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -2733,17 +2733,17 @@ class _GenerateProtocolActorCode(ipdl.as
         for actor in channelOpenedActors:
             # add the Alloc interface for actors created when a
             # new channel is opened
             actortype = _cxxBareType(actor.asType(), actor.side)
             self.cls.addstmt(StmtDecl(MethodDecl(
                 _allocMethod(actor.ptype, actor.side).name,
                 params=[ Decl(Type('Transport', ptr=1), 'transport'),
                          Decl(Type('ProcessId'), 'otherProcess') ],
-                ret=Type.BOOL,
+                ret=actortype,
                 virtual=1, pure=1)))
 
         # optional ActorDestroy() method; default is no-op
         self.cls.addstmts([
             Whitespace.NL,
             MethodDefn(MethodDecl(
                 _destroyMethod().name,
                 params=[ Decl(_DestroyReason.Type(), 'why') ],
--- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -20,31 +20,30 @@ namespace _ipdltest {
 // main process
 void
 TestBridgeMainParent::Main()
 {
     if (!SendStart())
         fail("sending Start");
 }
 
-bool
+PTestBridgeMainSubParent*
 TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
                                                     ProcessId otherProcess)
 {
     ProcessHandle h;
     if (!base::OpenProcessHandle(otherProcess, &h)) {
         return nullptr;
     }
 
     nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
     if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
         return nullptr;
     }
-    a.forget();
-    return true;
+    return a.forget();
 }
 
 void
 TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
 {
     if (NormalShutdown != why)
         fail("unexpected destruction!");  
     passed("ok");
@@ -172,35 +171,34 @@ TestBridgeSubChild::TestBridgeSubChild()
 bool
 TestBridgeSubChild::RecvPing()
 {
     if (!SendBridgeEm())
         fail("sending BridgeEm");
     return true;
 }
 
-bool
+PTestBridgeMainSubChild*
 TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport,
                                                  ProcessId otherProcess)
 {
     ProcessHandle h;
     if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return false;
+        return nullptr;
     }
 
     nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
     if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
-        return false;
+        return nullptr;
     }
 
     if (!a->SendHello())
         fail("sending Hello");
 
-    a.forget();
-    return true;
+    return a.forget();
 }
 
 void
 TestBridgeSubChild::ActorDestroy(ActorDestroyReason why)
 {
     if (NormalShutdown != why)
         fail("unexpected destruction!");
     QuitChild();
--- a/ipc/ipdl/test/cxx/TestBridgeMain.h
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.h
@@ -26,17 +26,17 @@ public:
     virtual ~TestBridgeMainParent() {}
 
     static bool RunTestInProcesses() { return true; }
     static bool RunTestInThreads() { return false; }
 
     void Main();
 
 protected:
-    virtual bool
+    virtual PTestBridgeMainSubParent*
     AllocPTestBridgeMainSubParent(Transport* transport,
                                   ProcessId otherProcess) MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 };
 
 class TestBridgeMainSubParent :
     public PTestBridgeMainSubParent
@@ -100,17 +100,17 @@ class TestBridgeSubChild :
 {
 public:
     TestBridgeSubChild();
     virtual ~TestBridgeSubChild() {}
 
 protected:
     virtual bool RecvPing() MOZ_OVERRIDE;
 
-    virtual bool
+    virtual PTestBridgeMainSubChild*
     AllocPTestBridgeMainSubChild(Transport* transport,
                                  ProcessId otherProcess) MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 };
 
 class TestBridgeMainSubChild :
     public PTestBridgeMainSubChild
--- a/ipc/ipdl/test/cxx/TestOpens.cpp
+++ b/ipc/ipdl/test/cxx/TestOpens.cpp
@@ -59,37 +59,37 @@ OpenParent(TestOpensOpenedParent* aParen
     // Open the actor on the off-main thread to park it there.
     // Messages will be delivered to this thread's message loop
     // instead of the main thread's.
     if (!aParent->Open(aTransport, aOtherProcess,
                        XRE_GetIOMessageLoop(), ipc::ParentSide))
         fail("opening Parent");
 }
 
-bool
+PTestOpensOpenedParent*
 TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
                                              ProcessId otherProcess)
 {
     gMainThread = MessageLoop::current();
 
     ProcessHandle h;
     if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return false;
+        return nullptr;
     }
 
     gParentThread = new Thread("ParentThread");
     if (!gParentThread->Start())
         fail("starting parent thread");
 
     TestOpensOpenedParent* a = new TestOpensOpenedParent(transport);
     gParentThread->message_loop()->PostTask(
         FROM_HERE,
         NewRunnableFunction(OpenParent, a, transport, h));
 
-    return true;
+    return a;
 }
 
 void
 TestOpensParent::ActorDestroy(ActorDestroyReason why)
 {
     // Stops the thread and joins it
     delete gParentThread;
 
@@ -172,37 +172,37 @@ OpenChild(TestOpensOpenedChild* aChild,
                       XRE_GetIOMessageLoop(), ipc::ChildSide))
         fail("opening Child");
 
     // Kick off the unit tests
     if (!aChild->SendHello())
         fail("sending Hello");
 }
 
-bool
+PTestOpensOpenedChild*
 TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
                                            ProcessId otherProcess)
 {
     gMainThread = MessageLoop::current();
 
     ProcessHandle h;
     if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return false;
+        return nullptr;
     }
 
     gChildThread = new Thread("ChildThread");
     if (!gChildThread->Start())
         fail("starting child thread");
 
     TestOpensOpenedChild* a = new TestOpensOpenedChild(transport);
     gChildThread->message_loop()->PostTask(
         FROM_HERE,
         NewRunnableFunction(OpenChild, a, transport, h));
 
-    return true;
+    return a;
 }
 
 void
 TestOpensChild::ActorDestroy(ActorDestroyReason why)
 {
     // Stops the thread and joins it
     delete gChildThread;
 
--- a/ipc/ipdl/test/cxx/TestOpens.h
+++ b/ipc/ipdl/test/cxx/TestOpens.h
@@ -22,17 +22,17 @@ public:
     virtual ~TestOpensParent() {}
 
     static bool RunTestInProcesses() { return true; }
     static bool RunTestInThreads() { return false; }
 
     void Main();
 
 protected:
-    virtual bool
+    virtual PTestOpensOpenedParent*
     AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 };
 
 } // namespace _ipdltest
 
 namespace _ipdltest2 {
@@ -65,17 +65,17 @@ class TestOpensChild : public PTestOpens
 {
 public:
     TestOpensChild();
     virtual ~TestOpensChild() {}
 
 protected:
     virtual bool RecvStart() MOZ_OVERRIDE;
 
-    virtual bool
+    virtual PTestOpensOpenedChild*
     AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 };
 
 } // namespace _ipdltest
 
 namespace _ipdltest2 {