Bumping manifests a=b2g-bump
[gecko.git] / ipc / glue / MessageLink.cpp
blob1c5ddeef6727fcaa16aef28cc195d731bdc816e0
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 "mozilla/Assertions.h"
19 #include "mozilla/DebugOnly.h"
20 #include "nsDebug.h"
21 #include "nsISupportsImpl.h"
22 #include "nsXULAppAPI.h"
24 using namespace mozilla;
25 using namespace std;
27 template<>
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
45 // the Transport.
46 template<>
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) { }
53 namespace mozilla {
54 namespace ipc {
56 MessageLink::MessageLink(MessageChannel *aChan)
57 : mChan(aChan)
61 MessageLink::~MessageLink()
63 #ifdef DEBUG
64 mChan = nullptr;
65 #endif
68 ProcessLink::ProcessLink(MessageChannel *aChan)
69 : MessageLink(aChan)
70 , mTransport(nullptr)
71 , mIOLoop(nullptr)
72 , mExistingListener(nullptr)
76 ProcessLink::~ProcessLink()
78 #ifdef DEBUG
79 mTransport = nullptr;
80 mIOLoop = nullptr;
81 mExistingListener = nullptr;
82 #endif
85 void
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
95 // appropriately
96 bool needOpen = true;
97 if(aIOLoop) {
98 // We're a child or using the new arguments. Either way, we
99 // need an open.
100 needOpen = true;
101 mChan->mSide = (aSide == UnknownSide) ? ChildSide : aSide;
102 } else {
103 NS_PRECONDITION(aSide == UnknownSide, "expected default side arg");
105 // parent
106 mChan->mSide = ParentSide;
107 needOpen = false;
108 aIOLoop = XRE_GetIOMessageLoop();
111 mIOLoop = aIOLoop;
113 NS_ASSERTION(mIOLoop, "need an IO loop");
114 NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
117 MonitorAutoLock lock(*mChan->mMonitor);
119 if (needOpen) {
120 // Transport::Connect() has not been called. Call it so
121 // we start polling our pipe and processing outgoing
122 // messages.
123 mIOLoop->PostTask(
124 FROM_HERE,
125 NewRunnableMethod(this, &ProcessLink::OnChannelOpened));
126 } else {
127 // Transport::Connect() has already been called. Take
128 // over the channel from the previous listener and process
129 // any queued messages.
130 mIOLoop->PostTask(
131 FROM_HERE,
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
141 // already frozen.
142 sleep(5);
144 #endif
146 // Should not wait here if something goes wrong with the channel.
147 while (!mChan->Connected() && mChan->mChannelState != ChannelError) {
148 mChan->mMonitor->Wait();
153 void
154 ProcessLink::EchoMessage(Message *msg)
156 mChan->AssertWorkerThread();
157 mChan->mMonitor->AssertCurrentThreadOwns();
159 mIOLoop->PostTask(
160 FROM_HERE,
161 NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg));
162 // OnEchoMessage takes ownership of |msg|
165 void
166 ProcessLink::SendMessage(Message *msg)
168 mChan->AssertWorkerThread();
169 mChan->mMonitor->AssertCurrentThreadOwns();
171 mIOLoop->PostTask(
172 FROM_HERE,
173 NewRunnableMethod(mTransport, &Transport::Send, msg));
176 void
177 ProcessLink::SendClose()
179 mChan->AssertWorkerThread();
180 mChan->mMonitor->AssertCurrentThreadOwns();
182 mIOLoop->PostTask(
183 FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnCloseChannel));
186 ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan)
187 : MessageLink(aChan),
188 mTargetChan(aTargetChan)
192 ThreadLink::~ThreadLink()
194 MOZ_ASSERT(mChan);
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:
202 // (Us) (Them)
203 // MessageChannel MessageChannel
204 // | ^ \ / ^ |
205 // | | X | |
206 // v | / \ | v
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.
214 if (mTargetChan) {
215 MOZ_ASSERT(mTargetChan->mLink);
216 static_cast<ThreadLink*>(mTargetChan->mLink)->mTargetChan = nullptr;
218 mTargetChan = nullptr;
221 void
222 ThreadLink::EchoMessage(Message *msg)
224 mChan->AssertWorkerThread();
225 mChan->mMonitor->AssertCurrentThreadOwns();
227 mChan->OnMessageReceivedFromLink(*msg);
228 delete msg;
231 void
232 ThreadLink::SendMessage(Message *msg)
234 mChan->AssertWorkerThread();
235 mChan->mMonitor->AssertCurrentThreadOwns();
237 if (mTargetChan)
238 mTargetChan->OnMessageReceivedFromLink(*msg);
239 delete msg;
242 void
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.
255 if (mTargetChan)
256 mTargetChan->OnChannelErrorFromLink();
259 bool
260 ThreadLink::Unsound_IsClosed() const
262 MonitorAutoLock lock(*mChan->mMonitor);
263 return mChan->mChannelState == ChannelClosed;
266 uint32_t
267 ThreadLink::Unsound_NumQueuedMessages() const
269 // ThreadLinks don't have a message queue.
270 return 0;
274 // The methods below run in the context of the IO thread
277 void
278 ProcessLink::OnMessageReceived(const Message& msg)
280 AssertIOThread();
281 NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
282 MonitorAutoLock lock(*mChan->mMonitor);
283 mChan->OnMessageReceivedFromLink(msg);
286 void
287 ProcessLink::OnEchoMessage(Message* msg)
289 AssertIOThread();
290 OnMessageReceived(*msg);
291 delete msg;
294 void
295 ProcessLink::OnChannelOpened()
297 AssertIOThread();
300 MonitorAutoLock lock(*mChan->mMonitor);
302 mExistingListener = mTransport->set_listener(this);
303 #ifdef DEBUG
304 if (mExistingListener) {
305 queue<Message> pending;
306 mExistingListener->GetQueuedMessages(pending);
307 MOZ_ASSERT(pending.empty());
309 #endif // DEBUG
311 mChan->mChannelState = ChannelOpening;
312 lock.Notify();
314 /*assert*/mTransport->Connect();
317 void
318 ProcessLink::OnTakeConnectedChannel()
320 AssertIOThread();
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);
332 lock.Notify();
335 // Dispatch whatever messages the previous listener had queued up.
336 while (!pending.empty()) {
337 OnMessageReceived(pending.front());
338 pending.pop();
342 void
343 ProcessLink::OnChannelConnected(int32_t peer_pid)
345 AssertIOThread();
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);
363 if (notifyChannel) {
364 mChan->OnChannelConnected(peer_pid);
368 void
369 ProcessLink::OnChannelError()
371 AssertIOThread();
373 MonitorAutoLock lock(*mChan->mMonitor);
375 MOZ_ALWAYS_TRUE(this == mTransport->set_listener(mExistingListener));
377 mChan->OnChannelErrorFromLink();
380 void
381 ProcessLink::OnCloseChannel()
383 AssertIOThread();
385 mTransport->Close();
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();
400 bool
401 ProcessLink::Unsound_IsClosed() const
403 return mTransport->Unsound_IsClosed();
406 uint32_t
407 ProcessLink::Unsound_NumQueuedMessages() const
409 return mTransport->Unsound_NumQueuedMessages();
412 } // namespace ipc
413 } // namespace mozilla