bug 523175: follow-up to 82549dbf71d8, fully enables the C++/IPDL side of Shmem and adds unit tests. r=bent
authorChris Jones <jones.chris.g@gmail.com>
Fri, 04 Dec 2009 12:45:21 -0600
changeset 36153 dc4201e0d9e5e503d5c94fae43371056eff397d2
parent 36152 5ffc5409203c649967a622154eb7437eeb10fdd8
child 36154 f4bcfc703ff92bcd7ac1c634c77ddc6f6b112dd1
push idunknown
push userunknown
push dateunknown
reviewersbent
bugs523175
milestone1.9.3a1pre
bug 523175: follow-up to 82549dbf71d8, fully enables the C++/IPDL side of Shmem and adds unit tests. r=bent
ipc/glue/ProtocolUtils.h
ipc/ipdl/ipdl/builtin.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/test/cxx/Makefile.in
ipc/ipdl/test/cxx/PTestShmem.ipdl
ipc/ipdl/test/cxx/TestShmem.cpp
ipc/ipdl/test/cxx/TestShmem.h
ipc/ipdl/test/cxx/ipdl.mk
ipc/ipdl/test/ipdl/error/shmem.ipdl
ipc/ipdl/test/ipdl/ok/shmem.ipdl
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -41,21 +41,23 @@
 #define mozilla_ipc_ProtocolUtils_h 1
 
 #include "base/process.h"
 #include "base/process_util.h"
 #include "chrome/common/ipc_message_utils.h"
 
 #include "prenv.h"
 
+#include "mozilla/ipc/Shmem.h"
 
 // WARNING: this takes into account the private, special-message-type
 // enum in ipc_channel.h.  They need to be kept in sync.
 namespace {
 enum {
+    SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 2,
     GOODBYE_MESSAGE_TYPE       = kuint16max - 1,
 };
 }
 
 namespace mozilla {
 namespace ipc {
 
 
@@ -84,26 +86,74 @@ public:
     virtual int32 RegisterID(ListenerT*, int32) = 0;
     virtual ListenerT* Lookup(int32) = 0;
     virtual void Unregister(int32) = 0;
     // XXX odd duck, acknowledged
     virtual ProcessHandle OtherProcess() const = 0;
 };
 
 
+// This message is automatically sent by IPDL-generated code when a
+// new shmem segment is allocated.  It should never be used directly.
+class __internal__ipdl__ShmemCreated : public IPC::Message
+{
+private:
+    typedef Shmem::id_t id_t;
+    typedef Shmem::SharedMemoryHandle SharedMemoryHandle;
+
+public:
+    enum { ID = SHMEM_CREATED_MESSAGE_TYPE };
+
+    __internal__ipdl__ShmemCreated(
+        int32 routingId,
+        const SharedMemoryHandle& aHandle,
+        const id_t& aIPDLId,
+        const size_t& aSize) :
+        IPC::Message(routingId, ID, PRIORITY_NORMAL)
+    {
+        IPC::WriteParam(this, aHandle);
+        IPC::WriteParam(this, aIPDLId);
+        IPC::WriteParam(this, aSize);
+    }
+
+    static bool Read(const Message* msg,
+                     SharedMemoryHandle* aHandle,
+                     id_t* aIPDLId,
+                     size_t* aSize)
+    {
+        void* iter = 0;
+        if (!IPC::ReadParam(msg, &iter, aHandle))
+            return false;
+        if (!IPC::ReadParam(msg, &iter, aIPDLId))
+            return false;
+        if (!IPC::ReadParam(msg, &iter, aSize))
+            return false;
+        msg->EndRead(iter);
+        return true;
+    }
+
+    void Log(const std::string& aPrefix,
+             FILE* aOutf) const
+    {
+        fputs("(special ShmemCreated msg)", aOutf);
+    }
+};
+
+
 inline bool
 LoggingEnabled()
 {
 #if defined(DEBUG)
     return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
 #else
     return false;
 #endif
 }
 
+
 } // namespace ipc
 } // namespace mozilla
 
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::ipc::ActorHandle>
--- a/ipc/ipdl/ipdl/builtin.py
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -51,16 +51,20 @@ Types = (
     'uint16_t',
     'int32_t',
     'uint32_t',
     'int64_t',
     'uint64_t',
     'intptr_t',
     'uintptr_t',
 
+    # stddef types
+    'size_t',
+    'ssize_t',
+
     # NSPR types
     'PRBool',
     'PRPackedBool'
     'PRInt8',
     'PRUint8',
     'PRInt16',
     'PRUint16',
     'PRInt32',
@@ -68,21 +72,23 @@ Types = (
     'PRInt64',
     'PRUint64',
     'PRSize',
 
     # Mozilla types: "less" standard things we know how serialize/deserialize
     'nsresult',
     'nsString',
     'nsCString',
+    'mozilla::ipc::Shmem'
 )
 
 
 Includes = (
     'base/basictypes.h',
     'prtime.h',
     'nscore.h',
     'IPCMessageStart.h',
     'IPC/IPCMessageUtils.h',
+    'nsAutoPtr.h',
     'nsStringGlue.h',
     'nsTArray.h',
     'mozilla/ipc/ProtocolUtils.h',
 )
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -76,16 +76,18 @@ class TypeVisitor:
             component.accept(self, *args)
 
     def visitArrayType(self, a, *args):
         a.basetype.accept(self, *args)
 
     def visitShmemType(self, s, *args):
         pass
 
+    def visitShmemChmodType(self, c, *args):
+        c.shmem.accept(self)
 
 class Type:
     def __cmp__(self, o):
         return cmp(self.fullname(), o.fullname())
     def __eq__(self, o):
         return (self.__class__ == o.__class__
                 and self.fullname() == o.fullname())
     def __hash__(self):
@@ -173,16 +175,17 @@ class IPDLType(Type):
     def isVisible(self): return True
     def isState(self): return False
     def isMessage(self): return False
     def isProtocol(self): return False
     def isActor(self): return False
     def isUnion(self): return False
     def isArray(self): return False
     def isShmem(self): return False
+    def isChmod(self): return False
 
     def isAsync(self): return self.sendSemantics is ASYNC
     def isSync(self): return self.sendSemantics is SYNC
     def isRpc(self): return self.sendSemantics is RPC
 
     def talksAsync(self): return True
     def talksSync(self): return self.isSync() or self.isRpc()
     def talksRpc(self): return self.isRpc()
@@ -298,32 +301,32 @@ class ShmemType(IPDLType):
         self.qname = qname
     def isShmem(self): return True
 
     def name(self):
         return self.qname.baseid
     def fullname(self):
         return str(self.qname)
 
-
 def iteractortypes(type):
     """Iterate over any actor(s) buried in |type|."""
     # XXX |yield| semantics makes it hard to use TypeVisitor
     if not type or not type.isIPDL():
         return
     elif type.isActor():
         yield type
     elif type.isArray():
         for actor in iteractortypes(type.basetype):
             yield actor
     elif type.isUnion():
         for c in type.components:
             for actor in iteractortypes(c):
                 yield actor
 
+
 def hasactor(type):
     """Return true iff |type| is an actor or has one buried within."""
     for _ in iteractortypes(type): return True
     return False
 
 def hasshmem(type):
     """Return true iff |type| is shmem or has it buried within."""
     class found: pass
@@ -893,17 +896,18 @@ class GatherDecls(TcheckVisitor):
     def _canonicalType(self, itype, typespec, chmodallowed=0):
         loc = typespec.loc
         
         if itype.isIPDL():
             if itype.isProtocol():
                 itype = ActorType(itype,
                                   state=typespec.state,
                                   nullable=typespec.nullable)
-            if chmodallowed and itype.isShmem():
+            # FIXME/cjones: ShmemChmod is disabled until bug 524193
+            if 0 and chmodallowed and itype.isShmem():
                 itype = ShmemChmodType(
                     itype,
                     myChmod=typespec.myChmod,
                     otherChmod=typespec.otherChmod)
 
         if ((typespec.myChmod or typespec.otherChmod)
             and not (itype.isIPDL() and (itype.isShmem() or itype.isChmod()))):
             self.error(
--- a/ipc/ipdl/test/cxx/Makefile.in
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -58,16 +58,17 @@ FORCE_STATIC_LIB = 1
 EXPORT_LIBRARY = 1
 
 # Please keep these organized in the order "easy"-to-"hard"
 IPDLTESTS = \
   TestSanity  \
   TestLatency \
   TestManyChildAllocs  \
   TestDesc \
+  TestShmem \
   TestShutdown \
   TestArrays \
   $(NULL)
 
 IPDLTESTSRCS = $(addsuffix .cpp,$(IPDLTESTS))
 IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS)))
 
 TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl
@@ -0,0 +1,22 @@
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestShmem {
+child:
+    Give(Shmem mem, size_t expectedSize);
+
+parent:
+    Take(Shmem mem, size_t expectedSize);
+    __delete__();
+
+
+state GIVING:
+    send Give goto TAKING;
+
+state TAKING:
+    recv Take goto TAKING;
+    recv __delete__;
+};
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.cpp
@@ -0,0 +1,75 @@
+#include "TestShmem.h"
+
+#include "IPDLUnitTests.h"      // fail etc.
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent
+
+void
+TestShmemParent::Main()
+{
+    Shmem mem;
+    size_t size = 12345;
+    if (!AllocShmem(size, &mem))
+        fail("can't alloc shmem");
+
+    if (mem.Size<char>() != size)
+        fail("shmem is wrong size: expected %lu, got %lu",
+             size, mem.Size<char>());
+
+    char* ptr = mem.get<char>();
+    memcpy(ptr, "Hello!", sizeof("Hello!"));
+    if (!SendGive(mem, size))
+        fail("can't send Give()");
+
+    // uncomment the following line for a (nondeterministic) surprise!
+    //char c1 = *ptr;  (void)c1;
+
+    // uncomment the following line for a deterministic surprise!
+    //char c2 = *mem.get<char>(); (void)c2;
+}
+
+
+bool
+TestShmemParent::RecvTake(Shmem& mem, const size_t& expectedSize)
+{
+    if (mem.Size<char>() != expectedSize)
+        fail("expected shmem size %lu, but it has size %lu",
+             expectedSize, mem.Size<char>());
+
+    if (strcmp(mem.get<char>(), "And yourself!"))
+        fail("expected message was not written");
+
+    Close();
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+// Child
+
+bool
+TestShmemChild::RecvGive(Shmem& mem, const size_t& expectedSize)
+{
+    if (mem.Size<char>() != expectedSize)
+        fail("expected shmem size %lu, but it has size %lu",
+             expectedSize, mem.Size<char>());
+
+    if (strcmp(mem.get<char>(), "Hello!"))
+        fail("expected message was not written");
+
+    memcpy(mem.get<char>(), "And yourself!", sizeof("And yourself!"));
+
+    if (!SendTake(mem, expectedSize))
+        fail("can't send Take()");
+
+    return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.h
@@ -0,0 +1,65 @@
+#ifndef mozilla__ipdltest_TestShmem_h
+#define mozilla__ipdltest_TestShmem_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestShmemParent.h"
+#include "mozilla/_ipdltest/PTestShmemChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestShmemParent :
+    public PTestShmemParent
+{
+public:
+    TestShmemParent() { }
+    virtual ~TestShmemParent() { }
+
+    void Main();
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvTake(
+            Shmem& mem,
+            const size_t& expectedSize);
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why)
+    {
+        if (NormalShutdown != why)
+            fail("unexpected destruction!");  
+        passed("ok");
+        QuitParent();
+    }
+};
+
+
+class TestShmemChild :
+    public PTestShmemChild
+{
+public:
+    TestShmemChild() { }
+    virtual ~TestShmemChild() { }
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvGive(
+            Shmem& mem,
+            const size_t& expectedSize);
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why)
+    {
+        if (NormalShutdown != why)
+            fail("unexpected destruction!");
+        QuitChild();
+    }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_TestShmem_h
--- a/ipc/ipdl/test/cxx/ipdl.mk
+++ b/ipc/ipdl/test/cxx/ipdl.mk
@@ -3,12 +3,13 @@ IPDLSRCS =					\
   PTestArraysSub.ipdl				\
   PTestDesc.ipdl				\
   PTestDescSub.ipdl				\
   PTestDescSubsub.ipdl				\
   PTestLatency.ipdl				\
   PTestManyChildAllocs.ipdl			\
   PTestManyChildAllocsSub.ipdl			\
   PTestSanity.ipdl				\
+  PTestShmem.ipdl				\
   PTestShutdown.ipdl				\
   PTestShutdownSub.ipdl				\
   PTestShutdownSubsub.ipdl			\
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/shmem.ipdl
@@ -0,0 +1,5 @@
+using mozilla::ipc::Shmem;      // redeclaration
+
+protocol shmem {
+child: Msg(Shmem s);
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/shmem.ipdl
@@ -0,0 +1,13 @@
+union Foo {
+    int;
+    Shmem;
+};
+
+rpc protocol shmem {
+parent:
+    Msg(Shmem s, Foo f);
+    sync SyncMsg(Shmem s, Foo f)
+        returns (Shmem t, Foo g);
+    rpc RPCMsg(Shmem s, Foo f)
+        returns (Shmem t, Foo g);
+};