Thu, 22 Jan 2015 13:21:57 +0100
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