|
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/. */ |
|
7 |
|
8 #include "mozilla/ipc/MessageLink.h" |
|
9 #include "mozilla/ipc/MessageChannel.h" |
|
10 #include "mozilla/ipc/BrowserProcessSubThread.h" |
|
11 #include "mozilla/ipc/ProtocolUtils.h" |
|
12 |
|
13 #ifdef MOZ_NUWA_PROCESS |
|
14 #include "ipc/Nuwa.h" |
|
15 #include "mozilla/Preferences.h" |
|
16 #endif |
|
17 |
|
18 #include "nsDebug.h" |
|
19 #include "nsISupportsImpl.h" |
|
20 #include "nsXULAppAPI.h" |
|
21 |
|
22 using namespace mozilla; |
|
23 using namespace std; |
|
24 |
|
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 }; |
|
31 |
|
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 }; |
|
50 |
|
51 namespace mozilla { |
|
52 namespace ipc { |
|
53 |
|
54 MessageLink::MessageLink(MessageChannel *aChan) |
|
55 : mChan(aChan) |
|
56 { |
|
57 } |
|
58 |
|
59 MessageLink::~MessageLink() |
|
60 { |
|
61 mChan = nullptr; |
|
62 } |
|
63 |
|
64 ProcessLink::ProcessLink(MessageChannel *aChan) |
|
65 : MessageLink(aChan), |
|
66 mExistingListener(nullptr) |
|
67 { |
|
68 } |
|
69 |
|
70 ProcessLink::~ProcessLink() |
|
71 { |
|
72 mIOLoop = 0; |
|
73 if (mTransport) { |
|
74 mTransport->set_listener(0); |
|
75 |
|
76 // we only hold a weak ref to the transport, which is "owned" |
|
77 // by GeckoChildProcess/GeckoThread |
|
78 mTransport = 0; |
|
79 } |
|
80 } |
|
81 |
|
82 void |
|
83 ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide) |
|
84 { |
|
85 NS_PRECONDITION(aTransport, "need transport layer"); |
|
86 |
|
87 // FIXME need to check for valid channel |
|
88 |
|
89 mTransport = aTransport; |
|
90 |
|
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"); |
|
101 |
|
102 // parent |
|
103 mChan->mSide = ParentSide; |
|
104 needOpen = false; |
|
105 aIOLoop = XRE_GetIOMessageLoop(); |
|
106 } |
|
107 |
|
108 mIOLoop = aIOLoop; |
|
109 |
|
110 NS_ASSERTION(mIOLoop, "need an IO loop"); |
|
111 NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop"); |
|
112 |
|
113 { |
|
114 MonitorAutoLock lock(*mChan->mMonitor); |
|
115 |
|
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 } |
|
131 |
|
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 |
|
142 |
|
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 } |
|
149 |
|
150 void |
|
151 ProcessLink::EchoMessage(Message *msg) |
|
152 { |
|
153 mChan->AssertWorkerThread(); |
|
154 mChan->mMonitor->AssertCurrentThreadOwns(); |
|
155 |
|
156 mIOLoop->PostTask( |
|
157 FROM_HERE, |
|
158 NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg)); |
|
159 // OnEchoMessage takes ownership of |msg| |
|
160 } |
|
161 |
|
162 void |
|
163 ProcessLink::SendMessage(Message *msg) |
|
164 { |
|
165 mChan->AssertWorkerThread(); |
|
166 mChan->mMonitor->AssertCurrentThreadOwns(); |
|
167 |
|
168 mIOLoop->PostTask( |
|
169 FROM_HERE, |
|
170 NewRunnableMethod(mTransport, &Transport::Send, msg)); |
|
171 } |
|
172 |
|
173 void |
|
174 ProcessLink::SendClose() |
|
175 { |
|
176 mChan->AssertWorkerThread(); |
|
177 mChan->mMonitor->AssertCurrentThreadOwns(); |
|
178 |
|
179 mIOLoop->PostTask( |
|
180 FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnCloseChannel)); |
|
181 } |
|
182 |
|
183 ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan) |
|
184 : MessageLink(aChan), |
|
185 mTargetChan(aTargetChan) |
|
186 { |
|
187 } |
|
188 |
|
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 } |
|
212 |
|
213 void |
|
214 ThreadLink::EchoMessage(Message *msg) |
|
215 { |
|
216 mChan->AssertWorkerThread(); |
|
217 mChan->mMonitor->AssertCurrentThreadOwns(); |
|
218 |
|
219 mChan->OnMessageReceivedFromLink(*msg); |
|
220 delete msg; |
|
221 } |
|
222 |
|
223 void |
|
224 ThreadLink::SendMessage(Message *msg) |
|
225 { |
|
226 mChan->AssertWorkerThread(); |
|
227 mChan->mMonitor->AssertCurrentThreadOwns(); |
|
228 |
|
229 if (mTargetChan) |
|
230 mTargetChan->OnMessageReceivedFromLink(*msg); |
|
231 delete msg; |
|
232 } |
|
233 |
|
234 void |
|
235 ThreadLink::SendClose() |
|
236 { |
|
237 mChan->AssertWorkerThread(); |
|
238 mChan->mMonitor->AssertCurrentThreadOwns(); |
|
239 |
|
240 mChan->mChannelState = ChannelClosed; |
|
241 |
|
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 } |
|
250 |
|
251 bool |
|
252 ThreadLink::Unsound_IsClosed() const |
|
253 { |
|
254 MonitorAutoLock lock(*mChan->mMonitor); |
|
255 return mChan->mChannelState == ChannelClosed; |
|
256 } |
|
257 |
|
258 uint32_t |
|
259 ThreadLink::Unsound_NumQueuedMessages() const |
|
260 { |
|
261 // ThreadLinks don't have a message queue. |
|
262 return 0; |
|
263 } |
|
264 |
|
265 // |
|
266 // The methods below run in the context of the IO thread |
|
267 // |
|
268 |
|
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 } |
|
277 |
|
278 void |
|
279 ProcessLink::OnEchoMessage(Message* msg) |
|
280 { |
|
281 AssertIOThread(); |
|
282 OnMessageReceived(*msg); |
|
283 delete msg; |
|
284 } |
|
285 |
|
286 void |
|
287 ProcessLink::OnChannelOpened() |
|
288 { |
|
289 mChan->AssertLinkThread(); |
|
290 { |
|
291 MonitorAutoLock lock(*mChan->mMonitor); |
|
292 |
|
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 |
|
301 |
|
302 mChan->mChannelState = ChannelOpening; |
|
303 lock.Notify(); |
|
304 } |
|
305 /*assert*/mTransport->Connect(); |
|
306 } |
|
307 |
|
308 void |
|
309 ProcessLink::OnTakeConnectedChannel() |
|
310 { |
|
311 AssertIOThread(); |
|
312 |
|
313 queue<Message> pending; |
|
314 { |
|
315 MonitorAutoLock lock(*mChan->mMonitor); |
|
316 |
|
317 mChan->mChannelState = ChannelConnected; |
|
318 |
|
319 mExistingListener = mTransport->set_listener(this); |
|
320 if (mExistingListener) { |
|
321 mExistingListener->GetQueuedMessages(pending); |
|
322 } |
|
323 lock.Notify(); |
|
324 } |
|
325 |
|
326 // Dispatch whatever messages the previous listener had queued up. |
|
327 while (!pending.empty()) { |
|
328 OnMessageReceived(pending.front()); |
|
329 pending.pop(); |
|
330 } |
|
331 } |
|
332 |
|
333 void |
|
334 ProcessLink::OnChannelConnected(int32_t peer_pid) |
|
335 { |
|
336 AssertIOThread(); |
|
337 |
|
338 { |
|
339 MonitorAutoLock lock(*mChan->mMonitor); |
|
340 mChan->mChannelState = ChannelConnected; |
|
341 mChan->mMonitor->Notify(); |
|
342 } |
|
343 |
|
344 if (mExistingListener) |
|
345 mExistingListener->OnChannelConnected(peer_pid); |
|
346 |
|
347 mChan->OnChannelConnected(peer_pid); |
|
348 } |
|
349 |
|
350 void |
|
351 ProcessLink::OnChannelError() |
|
352 { |
|
353 AssertIOThread(); |
|
354 MonitorAutoLock lock(*mChan->mMonitor); |
|
355 mChan->OnChannelErrorFromLink(); |
|
356 } |
|
357 |
|
358 void |
|
359 ProcessLink::OnCloseChannel() |
|
360 { |
|
361 AssertIOThread(); |
|
362 |
|
363 mTransport->Close(); |
|
364 |
|
365 MonitorAutoLock lock(*mChan->mMonitor); |
|
366 mChan->mChannelState = ChannelClosed; |
|
367 mChan->mMonitor->Notify(); |
|
368 } |
|
369 |
|
370 bool |
|
371 ProcessLink::Unsound_IsClosed() const |
|
372 { |
|
373 return mTransport->Unsound_IsClosed(); |
|
374 } |
|
375 |
|
376 uint32_t |
|
377 ProcessLink::Unsound_NumQueuedMessages() const |
|
378 { |
|
379 return mTransport->Unsound_NumQueuedMessages(); |
|
380 } |
|
381 |
|
382 } // namespace ipc |
|
383 } // namespace mozilla |