1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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
15 #include "mozilla/Preferences.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/DebugOnly.h"
21 #include "nsISupportsImpl.h"
22 #include "nsXULAppAPI.h"
24 using namespace mozilla
;
28 struct RunnableMethodTraits
<mozilla::ipc::ProcessLink
>
30 static void RetainCallee(mozilla::ipc::ProcessLink
* obj
) { }
31 static void ReleaseCallee(mozilla::ipc::ProcessLink
* obj
) { }
34 // We rely on invariants about the lifetime of the transport:
36 // - outlives this MessageChannel
37 // - deleted on the IO thread
39 // These invariants allow us to send messages directly through the
40 // transport without having to worry about orphaned Send() tasks on
41 // the IO thread touching MessageChannel memory after it's been deleted
42 // on the worker thread. We also don't need to refcount the
43 // Transport, because whatever task triggers its deletion only runs on
44 // the IO thread, and only runs after this MessageChannel is done with
47 struct RunnableMethodTraits
<mozilla::ipc::MessageChannel::Transport
>
49 static void RetainCallee(mozilla::ipc::MessageChannel::Transport
* obj
) { }
50 static void ReleaseCallee(mozilla::ipc::MessageChannel::Transport
* obj
) { }
56 MessageLink::MessageLink(MessageChannel
*aChan
)
61 MessageLink::~MessageLink()
68 ProcessLink::ProcessLink(MessageChannel
*aChan
)
72 , mExistingListener(nullptr)
76 ProcessLink::~ProcessLink()
81 mExistingListener
= nullptr;
86 ProcessLink::Open(mozilla::ipc::Transport
* aTransport
, MessageLoop
*aIOLoop
, Side aSide
)
88 NS_PRECONDITION(aTransport
, "need transport layer");
90 // FIXME need to check for valid channel
92 mTransport
= aTransport
;
94 // FIXME figure out whether we're in parent or child, grab IO loop
98 // We're a child or using the new arguments. Either way, we
101 mChan
->mSide
= (aSide
== UnknownSide
) ? ChildSide
: aSide
;
103 NS_PRECONDITION(aSide
== UnknownSide
, "expected default side arg");
106 mChan
->mSide
= ParentSide
;
108 aIOLoop
= XRE_GetIOMessageLoop();
113 NS_ASSERTION(mIOLoop
, "need an IO loop");
114 NS_ASSERTION(mChan
->mWorkerLoop
, "need a worker loop");
117 MonitorAutoLock
lock(*mChan
->mMonitor
);
120 // Transport::Connect() has not been called. Call it so
121 // we start polling our pipe and processing outgoing
125 NewRunnableMethod(this, &ProcessLink::OnChannelOpened
));
127 // Transport::Connect() has already been called. Take
128 // over the channel from the previous listener and process
129 // any queued messages.
132 NewRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel
));
135 #ifdef MOZ_NUWA_PROCESS
136 if (IsNuwaProcess() &&
137 Preferences::GetBool("dom.ipc.processPrelaunch.testMode")) {
138 // The pref value is turned on in a deadlock test against the Nuwa
139 // process. The sleep here makes it easy to trigger the deadlock
140 // that an IPC channel is still opening but the worker loop is
146 // Should not wait here if something goes wrong with the channel.
147 while (!mChan
->Connected() && mChan
->mChannelState
!= ChannelError
) {
148 mChan
->mMonitor
->Wait();
154 ProcessLink::EchoMessage(Message
*msg
)
156 mChan
->AssertWorkerThread();
157 mChan
->mMonitor
->AssertCurrentThreadOwns();
161 NewRunnableMethod(this, &ProcessLink::OnEchoMessage
, msg
));
162 // OnEchoMessage takes ownership of |msg|
166 ProcessLink::SendMessage(Message
*msg
)
168 mChan
->AssertWorkerThread();
169 mChan
->mMonitor
->AssertCurrentThreadOwns();
173 NewRunnableMethod(mTransport
, &Transport::Send
, msg
));
177 ProcessLink::SendClose()
179 mChan
->AssertWorkerThread();
180 mChan
->mMonitor
->AssertCurrentThreadOwns();
183 FROM_HERE
, NewRunnableMethod(this, &ProcessLink::OnCloseChannel
));
186 ThreadLink::ThreadLink(MessageChannel
*aChan
, MessageChannel
*aTargetChan
)
187 : MessageLink(aChan
),
188 mTargetChan(aTargetChan
)
192 ThreadLink::~ThreadLink()
195 MOZ_ASSERT(mChan
->mMonitor
);
196 MonitorAutoLock
lock(*mChan
->mMonitor
);
198 // Bug 848949: We need to prevent the other side
199 // from sending us any more messages to avoid Use-After-Free.
200 // The setup here is as shown:
203 // MessageChannel MessageChannel
207 // ThreadLink ThreadLink
209 // We want to null out the diagonal link from their ThreadLink
210 // to our MessageChannel. Note that we must hold the monitor so
211 // that we do this atomically with respect to them trying to send
212 // us a message. Since the channels share the same monitor this
213 // also protects against the two ~ThreadLink() calls racing.
215 MOZ_ASSERT(mTargetChan
->mLink
);
216 static_cast<ThreadLink
*>(mTargetChan
->mLink
)->mTargetChan
= nullptr;
218 mTargetChan
= nullptr;
222 ThreadLink::EchoMessage(Message
*msg
)
224 mChan
->AssertWorkerThread();
225 mChan
->mMonitor
->AssertCurrentThreadOwns();
227 mChan
->OnMessageReceivedFromLink(*msg
);
232 ThreadLink::SendMessage(Message
*msg
)
234 mChan
->AssertWorkerThread();
235 mChan
->mMonitor
->AssertCurrentThreadOwns();
238 mTargetChan
->OnMessageReceivedFromLink(*msg
);
243 ThreadLink::SendClose()
245 mChan
->AssertWorkerThread();
246 mChan
->mMonitor
->AssertCurrentThreadOwns();
248 mChan
->mChannelState
= ChannelClosed
;
250 // In a ProcessLink, we would close our half the channel. This
251 // would show up on the other side as an error on the I/O thread.
252 // The I/O thread would then invoke OnChannelErrorFromLink().
253 // As usual, we skip that process and just invoke the
254 // OnChannelErrorFromLink() method directly.
256 mTargetChan
->OnChannelErrorFromLink();
260 ThreadLink::Unsound_IsClosed() const
262 MonitorAutoLock
lock(*mChan
->mMonitor
);
263 return mChan
->mChannelState
== ChannelClosed
;
267 ThreadLink::Unsound_NumQueuedMessages() const
269 // ThreadLinks don't have a message queue.
274 // The methods below run in the context of the IO thread
278 ProcessLink::OnMessageReceived(const Message
& msg
)
281 NS_ASSERTION(mChan
->mChannelState
!= ChannelError
, "Shouldn't get here!");
282 MonitorAutoLock
lock(*mChan
->mMonitor
);
283 mChan
->OnMessageReceivedFromLink(msg
);
287 ProcessLink::OnEchoMessage(Message
* msg
)
290 OnMessageReceived(*msg
);
295 ProcessLink::OnChannelOpened()
300 MonitorAutoLock
lock(*mChan
->mMonitor
);
302 mExistingListener
= mTransport
->set_listener(this);
304 if (mExistingListener
) {
305 queue
<Message
> pending
;
306 mExistingListener
->GetQueuedMessages(pending
);
307 MOZ_ASSERT(pending
.empty());
311 mChan
->mChannelState
= ChannelOpening
;
314 /*assert*/mTransport
->Connect();
318 ProcessLink::OnTakeConnectedChannel()
322 queue
<Message
> pending
;
324 MonitorAutoLock
lock(*mChan
->mMonitor
);
326 mChan
->mChannelState
= ChannelConnected
;
328 mExistingListener
= mTransport
->set_listener(this);
329 if (mExistingListener
) {
330 mExistingListener
->GetQueuedMessages(pending
);
335 // Dispatch whatever messages the previous listener had queued up.
336 while (!pending
.empty()) {
337 OnMessageReceived(pending
.front());
343 ProcessLink::OnChannelConnected(int32_t peer_pid
)
347 bool notifyChannel
= false;
350 MonitorAutoLock
lock(*mChan
->mMonitor
);
351 // Only update channel state if its still thinks its opening. Do not
352 // force it into connected if it has errored out, started closing, etc.
353 if (mChan
->mChannelState
== ChannelOpening
) {
354 mChan
->mChannelState
= ChannelConnected
;
355 mChan
->mMonitor
->Notify();
356 notifyChannel
= true;
360 if (mExistingListener
)
361 mExistingListener
->OnChannelConnected(peer_pid
);
364 mChan
->OnChannelConnected(peer_pid
);
369 ProcessLink::OnChannelError()
373 MonitorAutoLock
lock(*mChan
->mMonitor
);
375 MOZ_ALWAYS_TRUE(this == mTransport
->set_listener(mExistingListener
));
377 mChan
->OnChannelErrorFromLink();
381 ProcessLink::OnCloseChannel()
387 MonitorAutoLock
lock(*mChan
->mMonitor
);
389 DebugOnly
<IPC::Channel::Listener
*> previousListener
=
390 mTransport
->set_listener(mExistingListener
);
392 // OnChannelError may have reset the listener already.
393 MOZ_ASSERT(previousListener
== this ||
394 previousListener
== mExistingListener
);
396 mChan
->mChannelState
= ChannelClosed
;
397 mChan
->mMonitor
->Notify();
401 ProcessLink::Unsound_IsClosed() const
403 return mTransport
->Unsound_IsClosed();
407 ProcessLink::Unsound_NumQueuedMessages() const
409 return mTransport
->Unsound_NumQueuedMessages();
413 } // namespace mozilla