ipc/glue/MessageLink.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: sw=4 ts=4 et :
     3  */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include "mozilla/ipc/MessageLink.h"
     9 #include "mozilla/ipc/MessageChannel.h"
    10 #include "mozilla/ipc/BrowserProcessSubThread.h"
    11 #include "mozilla/ipc/ProtocolUtils.h"
    13 #ifdef MOZ_NUWA_PROCESS
    14 #include "ipc/Nuwa.h"
    15 #include "mozilla/Preferences.h"
    16 #endif
    18 #include "nsDebug.h"
    19 #include "nsISupportsImpl.h"
    20 #include "nsXULAppAPI.h"
    22 using namespace mozilla;
    23 using namespace std;
    25 template<>
    26 struct RunnableMethodTraits<mozilla::ipc::ProcessLink>
    27 {
    28     static void RetainCallee(mozilla::ipc::ProcessLink* obj) { }
    29     static void ReleaseCallee(mozilla::ipc::ProcessLink* obj) { }
    30 };
    32 // We rely on invariants about the lifetime of the transport:
    33 //
    34 //  - outlives this MessageChannel
    35 //  - deleted on the IO thread
    36 //
    37 // These invariants allow us to send messages directly through the
    38 // transport without having to worry about orphaned Send() tasks on
    39 // the IO thread touching MessageChannel memory after it's been deleted
    40 // on the worker thread.  We also don't need to refcount the
    41 // Transport, because whatever task triggers its deletion only runs on
    42 // the IO thread, and only runs after this MessageChannel is done with
    43 // the Transport.
    44 template<>
    45 struct RunnableMethodTraits<mozilla::ipc::MessageChannel::Transport>
    46 {
    47     static void RetainCallee(mozilla::ipc::MessageChannel::Transport* obj) { }
    48     static void ReleaseCallee(mozilla::ipc::MessageChannel::Transport* obj) { }
    49 };
    51 namespace mozilla {
    52 namespace ipc {
    54 MessageLink::MessageLink(MessageChannel *aChan)
    55   : mChan(aChan)
    56 {
    57 }
    59 MessageLink::~MessageLink()
    60 {
    61     mChan = nullptr;
    62 }
    64 ProcessLink::ProcessLink(MessageChannel *aChan)
    65   : MessageLink(aChan),
    66     mExistingListener(nullptr)
    67 {
    68 }
    70 ProcessLink::~ProcessLink()
    71 {
    72     mIOLoop = 0;
    73     if (mTransport) {
    74         mTransport->set_listener(0);
    76         // we only hold a weak ref to the transport, which is "owned"
    77         // by GeckoChildProcess/GeckoThread
    78         mTransport = 0;
    79     }
    80 }
    82 void 
    83 ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide)
    84 {
    85     NS_PRECONDITION(aTransport, "need transport layer");
    87     // FIXME need to check for valid channel
    89     mTransport = aTransport;
    91     // FIXME figure out whether we're in parent or child, grab IO loop
    92     // appropriately
    93     bool needOpen = true;
    94     if(aIOLoop) {
    95         // We're a child or using the new arguments.  Either way, we
    96         // need an open.
    97         needOpen = true;
    98         mChan->mSide = (aSide == UnknownSide) ? ChildSide : aSide;
    99     } else {
   100         NS_PRECONDITION(aSide == UnknownSide, "expected default side arg");
   102         // parent
   103         mChan->mSide = ParentSide;
   104         needOpen = false;
   105         aIOLoop = XRE_GetIOMessageLoop();
   106     }
   108     mIOLoop = aIOLoop;
   110     NS_ASSERTION(mIOLoop, "need an IO loop");
   111     NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
   113     {
   114         MonitorAutoLock lock(*mChan->mMonitor);
   116         if (needOpen) {
   117             // Transport::Connect() has not been called.  Call it so
   118             // we start polling our pipe and processing outgoing
   119             // messages.
   120             mIOLoop->PostTask(
   121                 FROM_HERE,
   122                 NewRunnableMethod(this, &ProcessLink::OnChannelOpened));
   123         } else {
   124             // Transport::Connect() has already been called.  Take
   125             // over the channel from the previous listener and process
   126             // any queued messages.
   127             mIOLoop->PostTask(
   128                 FROM_HERE,
   129                 NewRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel));
   130         }
   132 #ifdef MOZ_NUWA_PROCESS
   133         if (IsNuwaProcess() &&
   134             Preferences::GetBool("dom.ipc.processPrelaunch.testMode")) {
   135             // The pref value is turned on in a deadlock test against the Nuwa
   136             // process. The sleep here makes it easy to trigger the deadlock
   137             // that an IPC channel is still opening but the worker loop is
   138             // already frozen.
   139             sleep(5);
   140         }
   141 #endif
   143         // Should not wait here if something goes wrong with the channel.
   144         while (!mChan->Connected() && mChan->mChannelState != ChannelError) {
   145             mChan->mMonitor->Wait();
   146         }
   147     }
   148 }
   150 void
   151 ProcessLink::EchoMessage(Message *msg)
   152 {
   153     mChan->AssertWorkerThread();
   154     mChan->mMonitor->AssertCurrentThreadOwns();
   156     mIOLoop->PostTask(
   157         FROM_HERE,
   158         NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg));
   159     // OnEchoMessage takes ownership of |msg|
   160 }
   162 void
   163 ProcessLink::SendMessage(Message *msg)
   164 {
   165     mChan->AssertWorkerThread();
   166     mChan->mMonitor->AssertCurrentThreadOwns();
   168     mIOLoop->PostTask(
   169         FROM_HERE,
   170         NewRunnableMethod(mTransport, &Transport::Send, msg));
   171 }
   173 void
   174 ProcessLink::SendClose()
   175 {
   176     mChan->AssertWorkerThread();
   177     mChan->mMonitor->AssertCurrentThreadOwns();
   179     mIOLoop->PostTask(
   180         FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnCloseChannel));
   181 }
   183 ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan)
   184   : MessageLink(aChan),
   185     mTargetChan(aTargetChan)
   186 {
   187 }
   189 ThreadLink::~ThreadLink()
   190 {
   191     // :TODO: MonitorAutoLock lock(*mChan->mMonitor);
   192     // Bug 848949: We need to prevent the other side
   193     // from sending us any more messages to avoid Use-After-Free.
   194     // The setup here is as shown:
   195     //
   196     //          (Us)         (Them)
   197     //       MessageChannel  MessageChannel
   198     //         |  ^     \ /     ^ |
   199     //         |  |      X      | |
   200     //         v  |     / \     | v
   201     //        ThreadLink   ThreadLink
   202     //
   203     // We want to null out the diagonal link from their ThreadLink
   204     // to our MessageChannel.  Note that we must hold the monitor so
   205     // that we do this atomically with respect to them trying to send
   206     // us a message.
   207     if (mTargetChan) {
   208         static_cast<ThreadLink*>(mTargetChan->mLink)->mTargetChan = 0;
   209     }
   210     mTargetChan = 0;
   211 }
   213 void
   214 ThreadLink::EchoMessage(Message *msg)
   215 {
   216     mChan->AssertWorkerThread();
   217     mChan->mMonitor->AssertCurrentThreadOwns();
   219     mChan->OnMessageReceivedFromLink(*msg);
   220     delete msg;
   221 }
   223 void
   224 ThreadLink::SendMessage(Message *msg)
   225 {
   226     mChan->AssertWorkerThread();
   227     mChan->mMonitor->AssertCurrentThreadOwns();
   229     if (mTargetChan)
   230         mTargetChan->OnMessageReceivedFromLink(*msg);
   231     delete msg;
   232 }
   234 void
   235 ThreadLink::SendClose()
   236 {
   237     mChan->AssertWorkerThread();
   238     mChan->mMonitor->AssertCurrentThreadOwns();
   240     mChan->mChannelState = ChannelClosed;
   242     // In a ProcessLink, we would close our half the channel.  This
   243     // would show up on the other side as an error on the I/O thread.
   244     // The I/O thread would then invoke OnChannelErrorFromLink().
   245     // As usual, we skip that process and just invoke the
   246     // OnChannelErrorFromLink() method directly.
   247     if (mTargetChan)
   248         mTargetChan->OnChannelErrorFromLink();
   249 }
   251 bool
   252 ThreadLink::Unsound_IsClosed() const
   253 {
   254     MonitorAutoLock lock(*mChan->mMonitor);
   255     return mChan->mChannelState == ChannelClosed;
   256 }
   258 uint32_t
   259 ThreadLink::Unsound_NumQueuedMessages() const
   260 {
   261     // ThreadLinks don't have a message queue.
   262     return 0;
   263 }
   265 //
   266 // The methods below run in the context of the IO thread
   267 //
   269 void
   270 ProcessLink::OnMessageReceived(const Message& msg)
   271 {
   272     AssertIOThread();
   273     NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
   274     MonitorAutoLock lock(*mChan->mMonitor);
   275     mChan->OnMessageReceivedFromLink(msg);
   276 }
   278 void
   279 ProcessLink::OnEchoMessage(Message* msg)
   280 {
   281     AssertIOThread();
   282     OnMessageReceived(*msg);
   283     delete msg;
   284 }
   286 void
   287 ProcessLink::OnChannelOpened()
   288 {
   289     mChan->AssertLinkThread();
   290     {
   291         MonitorAutoLock lock(*mChan->mMonitor);
   293         mExistingListener = mTransport->set_listener(this);
   294 #ifdef DEBUG
   295         if (mExistingListener) {
   296             queue<Message> pending;
   297             mExistingListener->GetQueuedMessages(pending);
   298             MOZ_ASSERT(pending.empty());
   299         }
   300 #endif  // DEBUG
   302         mChan->mChannelState = ChannelOpening;
   303         lock.Notify();
   304     }
   305     /*assert*/mTransport->Connect();
   306 }
   308 void
   309 ProcessLink::OnTakeConnectedChannel()
   310 {
   311     AssertIOThread();
   313     queue<Message> pending;
   314     {
   315         MonitorAutoLock lock(*mChan->mMonitor);
   317         mChan->mChannelState = ChannelConnected;
   319         mExistingListener = mTransport->set_listener(this);
   320         if (mExistingListener) {
   321             mExistingListener->GetQueuedMessages(pending);
   322         }
   323         lock.Notify();
   324     }
   326     // Dispatch whatever messages the previous listener had queued up.
   327     while (!pending.empty()) {
   328         OnMessageReceived(pending.front());
   329         pending.pop();
   330     }
   331 }
   333 void
   334 ProcessLink::OnChannelConnected(int32_t peer_pid)
   335 {
   336     AssertIOThread();
   338     {
   339         MonitorAutoLock lock(*mChan->mMonitor);
   340         mChan->mChannelState = ChannelConnected;
   341         mChan->mMonitor->Notify();
   342     }
   344     if (mExistingListener)
   345         mExistingListener->OnChannelConnected(peer_pid);
   347     mChan->OnChannelConnected(peer_pid);
   348 }
   350 void
   351 ProcessLink::OnChannelError()
   352 {
   353     AssertIOThread();
   354     MonitorAutoLock lock(*mChan->mMonitor);
   355     mChan->OnChannelErrorFromLink();
   356 }
   358 void
   359 ProcessLink::OnCloseChannel()
   360 {
   361     AssertIOThread();
   363     mTransport->Close();
   365     MonitorAutoLock lock(*mChan->mMonitor);
   366     mChan->mChannelState = ChannelClosed;
   367     mChan->mMonitor->Notify();
   368 }
   370 bool
   371 ProcessLink::Unsound_IsClosed() const
   372 {
   373     return mTransport->Unsound_IsClosed();
   374 }
   376 uint32_t
   377 ProcessLink::Unsound_NumQueuedMessages() const
   378 {
   379     return mTransport->Unsound_NumQueuedMessages();
   380 }
   382 } // namespace ipc
   383 } // namespace mozilla

mercurial