Bug 1092010 - Add and use move semantics for Pickle and IPC::Message. r=dvander, a=lsblakk
authorNathan Froyd <froydnj@mozilla.com>
Thu, 06 Nov 2014 12:46:00 -0500
changeset 233815 975ffc36f1de91613357086b06aa90fead87c72c
parent 233814 2068eecf1f2aa490ef0dcbebc038ff31a27945b7
child 233816 2ec16d58f770f8d94281e1b35207d6932a60c5bc
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander, lsblakk
bugs1092010
milestone35.0a2
Bug 1092010 - Add and use move semantics for Pickle and IPC::Message. r=dvander, a=lsblakk
ipc/chromium/src/base/pickle.cc
ipc/chromium/src/base/pickle.h
ipc/chromium/src/chrome/common/ipc_message.cc
ipc/chromium/src/chrome/common/ipc_message.h
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -139,16 +139,26 @@ Pickle::Pickle(const Pickle& other)
   uint32_t payload_size = header_size_ + other.header_->payload_size;
   bool resized = Resize(payload_size);
   if (!resized) {
     NS_ABORT_OOM(payload_size);
   }
   memcpy(header_, other.header_, payload_size);
 }
 
+Pickle::Pickle(Pickle&& other)
+  : header_(other.header_),
+    header_size_(other.header_size_),
+    capacity_(other.capacity_),
+    variable_buffer_offset_(other.variable_buffer_offset_) {
+  other.header_ = NULL;
+  other.capacity_ = 0;
+  other.variable_buffer_offset_ = 0;
+}
+
 Pickle::~Pickle() {
   if (capacity_ != kCapacityReadOnly)
     free(header_);
 }
 
 Pickle& Pickle::operator=(const Pickle& other) {
   if (header_size_ != other.header_size_ && capacity_ != kCapacityReadOnly) {
     free(header_);
@@ -159,16 +169,24 @@ Pickle& Pickle::operator=(const Pickle& 
   if (!resized) {
     NS_ABORT_OOM(other.header_size_ + other.header_->payload_size);
   }
   memcpy(header_, other.header_, header_size_ + other.header_->payload_size);
   variable_buffer_offset_ = other.variable_buffer_offset_;
   return *this;
 }
 
+Pickle& Pickle::operator=(Pickle&& other) {
+  std::swap(header_, other.header_);
+  std::swap(header_size_, other.header_size_);
+  std::swap(capacity_, other.capacity_);
+  std::swap(variable_buffer_offset_, other.variable_buffer_offset_);
+  return *this;
+}
+
 bool Pickle::ReadBool(void** iter, bool* result) const {
   DCHECK(iter);
 
   int tmp;
   if (!ReadInt(iter, &tmp))
     return false;
   DCHECK(0 == tmp || 1 == tmp);
   *result = tmp ? true : false;
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -44,19 +44,23 @@ class Pickle {
   // instead the data is merely referenced by this Pickle.  Only const methods
   // should be used on the Pickle when initialized this way.  The header
   // padding size is deduced from the data length.
   Pickle(const char* data, int data_len);
 
   // Initializes a Pickle as a deep copy of another Pickle.
   Pickle(const Pickle& other);
 
+  Pickle(Pickle&& other);
+
   // Performs a deep copy.
   Pickle& operator=(const Pickle& other);
 
+  Pickle& operator=(Pickle&& other);
+
   // Returns the size of the Pickle's data.
   int size() const { return static_cast<int>(header_size_ +
                                              header_->payload_size); }
 
   // Returns the data for this Pickle.
   const void* data() const { return header_; }
 
   // Methods for reading the payload of the Pickle.  To read from the start of
--- a/ipc/chromium/src/chrome/common/ipc_message.cc
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -9,16 +9,18 @@
 
 #if defined(OS_POSIX)
 #include "chrome/common/file_descriptor_set_posix.h"
 #endif
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
+#include "mozilla/Move.h"
+
 #ifdef MOZ_TASK_TRACER
 using namespace mozilla::tasktracer;
 #endif
 
 namespace IPC {
 
 //------------------------------------------------------------------------------
 
@@ -75,16 +77,28 @@ Message::Message(const Message& other) :
 #endif
 #ifdef MOZ_TASK_TRACER
   header()->source_event_id = other.header()->source_event_id;
   header()->parent_task_id = other.header()->parent_task_id;
   header()->source_event_type = other.header()->source_event_type;
 #endif
 }
 
+Message::Message(Message&& other) : Pickle(mozilla::Move(other)) {
+  InitLoggingVariables(other.name_);
+#if defined(OS_POSIX)
+  file_descriptor_set_ = other.file_descriptor_set_.forget();
+#endif
+#ifdef MOZ_TASK_TRACER
+  header()->source_event_id = other.header()->source_event_id;
+  header()->parent_task_id = other.header()->parent_task_id;
+  header()->source_event_type = other.header()->source_event_type;
+#endif
+}
+
 void Message::InitLoggingVariables(const char* const name) {
   name_ = name;
 #ifdef IPC_MESSAGE_LOG_ENABLED
   received_time_ = 0;
   dont_log_ = false;
   log_data_ = NULL;
 #endif
 }
@@ -98,16 +112,30 @@ Message& Message::operator=(const Messag
 #ifdef MOZ_TASK_TRACER
   header()->source_event_id = other.header()->source_event_id;
   header()->parent_task_id = other.header()->parent_task_id;
   header()->source_event_type = other.header()->source_event_type;
 #endif
   return *this;
 }
 
+Message& Message::operator=(Message&& other) {
+  *static_cast<Pickle*>(this) = mozilla::Move(other);
+  InitLoggingVariables(other.name_);
+#if defined(OS_POSIX)
+  file_descriptor_set_.swap(other.file_descriptor_set_);
+#endif
+#ifdef MOZ_TASK_TRACER
+  std::swap(header()->source_event_id, other.header()->source_event_id);
+  std::swap(header()->parent_task_id, other.header()->parent_task_id);
+  std::swap(header()->source_event_type, other.header()->source_event_type);
+#endif
+  return *this;
+}
+
 #ifdef IPC_MESSAGE_LOG_ENABLED
 void Message::set_sent_time(int64_t time) {
   DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0);
   header()->flags |= HAS_SENT_TIME_BIT;
   WriteInt64(time);
 }
 
 int64_t Message::sent_time() const {
--- a/ipc/chromium/src/chrome/common/ipc_message.h
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -74,17 +74,19 @@ class Message : public Pickle {
           const char* const name="???");
 
   // Initializes a message from a const block of data.  The data is not copied;
   // instead the data is merely referenced by this message.  Only const methods
   // should be used on the message when initialized this way.
   Message(const char* data, int data_len);
 
   Message(const Message& other);
+  Message(Message&& other);
   Message& operator=(const Message& other);
+  Message& operator=(Message&& other);
 
   PriorityValue priority() const {
     return static_cast<PriorityValue>(header()->flags & PRIORITY_MASK);
   }
 
   void set_priority(int prio) {
     DCHECK((prio & ~PRIORITY_MASK) == 0);
     header()->flags = (header()->flags & ~PRIORITY_MASK) | prio;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -696,21 +696,32 @@ struct AutoDeferMessages
 {
     typedef IPC::Message Message;
 
     std::deque<Message>& mQueue;
     mozilla::Vector<Message> mDeferred;
 
     AutoDeferMessages(std::deque<Message>& queue) : mQueue(queue) {}
     ~AutoDeferMessages() {
-        mQueue.insert(mQueue.begin(), mDeferred.begin(), mDeferred.end());
+        if (mDeferred.empty()) {
+            return;
+        }
+
+        // We'd like to use queue.insert, but that copies, and
+        // std::make_move_iterator is not available in stlport.
+        Message dummy;
+        for (size_t i = mDeferred.length(); i != 0; --i) {
+            mQueue.push_front(dummy);
+            Message& first = mQueue.front();
+            first = Move(mDeferred[i-1]);
+        }
     }
 
-    void Defer(Message aMsg) {
-        mDeferred.append(aMsg);
+    void Defer(Message&& aMsg) {
+        mDeferred.append(Move(aMsg));
     }
 };
 
 bool
 MessageChannel::SendAndWait(Message* aMsg, Message* aReply)
 {
     mMonitor->AssertCurrentThreadOwns();
 
@@ -727,37 +738,37 @@ MessageChannel::SendAndWait(Message* aMs
     DebugOnly<msgid_t> replyType = msg->type() + 1;
 
     mLink->SendMessage(msg.forget());
 
     AutoDeferMessages defer(mPending);
 
     while (true) {
         while (!mPending.empty()) {
-            Message msg = mPending.front();
+            Message msg = Move(mPending.front());
             mPending.pop_front();
             if (ShouldDeferMessage(msg))
-                defer.Defer(msg);
+                defer.Defer(Move(msg));
             else
                 ProcessPendingRequest(msg);
         }
 
         // See if we've received a reply.
         if (mRecvd) {
             MOZ_ASSERT(mRecvd->is_reply(), "expected reply");
 
             if (mRecvd->is_reply_error()) {
                 mRecvd = nullptr;
                 return false;
             }
 
             MOZ_ASSERT(mRecvd->type() == replyType, "wrong reply type");
             MOZ_ASSERT(mRecvd->seqno() == replySeqno);
 
-            *aReply = *mRecvd;
+            *aReply = Move(*mRecvd);
             mRecvd = nullptr;
             return true;
         }
 
         bool maybeTimedOut = !WaitForSyncNotify();
 
         if (!Connected()) {
             ReportConnectionError("MessageChannel::SendAndWait");
@@ -838,20 +849,20 @@ MessageChannel::Call(Message* aMsg, Mess
         }
 
         Message recvd;
         MessageMap::iterator it;
 
         if ((it = mOutOfTurnReplies.find(mInterruptStack.top().seqno()))
             != mOutOfTurnReplies.end())
         {
-            recvd = it->second;
+            recvd = Move(it->second);
             mOutOfTurnReplies.erase(it);
         } else if (!mPending.empty()) {
-            recvd = mPending.front();
+            recvd = Move(mPending.front());
             mPending.pop_front();
         } else {
             // because of subtleties with nested event loops, it's possible
             // that we got here and nothing happened.  or, we might have a
             // deferred in-call that needs to be processed.  either way, we
             // won't break the inner while loop again until something new
             // happens.
             continue;
@@ -882,41 +893,42 @@ MessageChannel::Call(Message* aMsg, Mess
             {
                 const Message &outcall = mInterruptStack.top();
 
                 // Note, In the parent, sequence numbers increase from 0, and
                 // in the child, they decrease from 0.
                 if ((mSide == ChildSide && recvd.seqno() > outcall.seqno()) ||
                     (mSide != ChildSide && recvd.seqno() < outcall.seqno()))
                 {
-                    mOutOfTurnReplies[recvd.seqno()] = recvd;
+                    mOutOfTurnReplies[recvd.seqno()] = Move(recvd);
                     continue;
                 }
 
                 IPC_ASSERT(recvd.is_reply_error() ||
                            (recvd.type() == (outcall.type() + 1) &&
                             recvd.seqno() == outcall.seqno()),
                            "somebody's misbehavin'", true);
             }
 
             // We received a reply to our most recent outstanding call. Pop
             // this frame and return the reply.
             mInterruptStack.pop();
 
-            if (!recvd.is_reply_error()) {
-                *aReply = recvd;
+            bool is_reply_error = recvd.is_reply_error();
+            if (!is_reply_error) {
+                *aReply = Move(recvd);
             }
 
             // If we have no more pending out calls waiting on replies, then
             // the reply queue should be empty.
             IPC_ASSERT(!mInterruptStack.empty() || mOutOfTurnReplies.empty(),
                        "still have pending replies with no pending out-calls",
                        true);
 
-            return !recvd.is_reply_error();
+            return !is_reply_error;
         }
 
         // Dispatch an Interrupt in-call. Snapshot the current stack depth while we
         // own the monitor.
         size_t stackDepth = InterruptStackDepth();
         {
             MonitorAutoUnlock unlock(*mMonitor);
 
@@ -942,17 +954,17 @@ MessageChannel::InterruptEventOccurred()
     return (!Connected() ||
             !mPending.empty() ||
             (!mOutOfTurnReplies.empty() &&
              mOutOfTurnReplies.find(mInterruptStack.top().seqno()) !=
              mOutOfTurnReplies.end()));
 }
 
 bool
-MessageChannel::ProcessPendingRequest(Message aUrgent)
+MessageChannel::ProcessPendingRequest(const Message& aUrgent)
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
 
     // Note that it is possible we could have sent a sync message at
     // the same time the parent process sent an urgent message, and
     // therefore mPendingUrgentRequest is set *and* mRecvd is set as
     // well, because the link thread received both before the worker
@@ -997,17 +1009,17 @@ MessageChannel::DequeueOne(Message *recv
     }
 
     if (!mDeferred.empty())
         MaybeUndeferIncall();
 
     if (mPending.empty())
         return false;
 
-    *recvd = mPending.front();
+    *recvd = Move(mPending.front());
     mPending.pop_front();
     return true;
 }
 
 bool
 MessageChannel::OnMaybeDequeueOne()
 {
     AssertWorkerThread();
@@ -1017,17 +1029,17 @@ MessageChannel::OnMaybeDequeueOne()
 
     MonitorAutoLock lock(*mMonitor);
     if (!DequeueOne(&recvd))
         return false;
 
     if (IsOnCxxStack() && recvd.is_interrupt() && recvd.is_reply()) {
         // We probably just received a reply in a nested loop for an
         // Interrupt call sent before entering that loop.
-        mOutOfTurnReplies[recvd.seqno()] = recvd;
+        mOutOfTurnReplies[recvd.seqno()] = Move(recvd);
         return false;
     }
 
     {
         // We should not be in a transaction yet if we're not blocked.
         MOZ_ASSERT(mCurrentTransaction == 0);
         AutoEnterTransaction transaction(this, &recvd);
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -223,17 +223,17 @@ class MessageChannel : HasResultCodes
     // soon as a reply has been received, or an error has occurred.
     //
     // Note that while the child is blocked waiting for a sync reply, it can wake
     // up to process urgent calls from the parent.
     bool SendAndWait(Message* aMsg, Message* aReply);
 
     bool InterruptEventOccurred();
 
-    bool ProcessPendingRequest(Message aUrgent);
+    bool ProcessPendingRequest(const Message& aUrgent);
 
     void MaybeUndeferIncall();
     void EnqueuePendingMessages();
 
     // Executed on the worker thread. Dequeues one pending message.
     bool OnMaybeDequeueOne();
     bool DequeueOne(Message *recvd);
 
@@ -505,17 +505,17 @@ class MessageChannel : HasResultCodes
        explicit AutoEnterTransaction(MessageChannel *aChan)
         : mChan(aChan),
           mOldTransaction(mChan->mCurrentTransaction)
        {
            mChan->mMonitor->AssertCurrentThreadOwns();
            if (mChan->mCurrentTransaction == 0)
                mChan->mCurrentTransaction = mChan->NextSeqno();
        }
-       explicit AutoEnterTransaction(MessageChannel *aChan, Message *message)
+       explicit AutoEnterTransaction(MessageChannel *aChan, const Message *message)
         : mChan(aChan),
           mOldTransaction(mChan->mCurrentTransaction)
        {
            mChan->mMonitor->AssertCurrentThreadOwns();
 
            if (!message->is_sync())
                return;