1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: sw=4 ts=4 et :
3 : */
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Plugin App.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Chris Jones <jones.chris.g@gmail.com>
21 : * Portions created by the Initial Developer are Copyright (C) 2009
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/ipc/AsyncChannel.h"
41 : #include "mozilla/ipc/BrowserProcessSubThread.h"
42 : #include "mozilla/ipc/ProtocolUtils.h"
43 :
44 : #include "nsDebug.h"
45 : #include "nsTraceRefcnt.h"
46 : #include "nsXULAppAPI.h"
47 :
48 : using mozilla::MonitorAutoLock;
49 :
50 : template<>
51 : struct RunnableMethodTraits<mozilla::ipc::AsyncChannel>
52 0 : {
53 0 : static void RetainCallee(mozilla::ipc::AsyncChannel* obj) { }
54 0 : static void ReleaseCallee(mozilla::ipc::AsyncChannel* obj) { }
55 : };
56 :
57 : template<>
58 : struct RunnableMethodTraits<mozilla::ipc::AsyncChannel::ProcessLink>
59 0 : {
60 0 : static void RetainCallee(mozilla::ipc::AsyncChannel::ProcessLink* obj) { }
61 0 : static void ReleaseCallee(mozilla::ipc::AsyncChannel::ProcessLink* obj) { }
62 : };
63 :
64 : // We rely on invariants about the lifetime of the transport:
65 : //
66 : // - outlives this AsyncChannel
67 : // - deleted on the IO thread
68 : //
69 : // These invariants allow us to send messages directly through the
70 : // transport without having to worry about orphaned Send() tasks on
71 : // the IO thread touching AsyncChannel memory after it's been deleted
72 : // on the worker thread. We also don't need to refcount the
73 : // Transport, because whatever task triggers its deletion only runs on
74 : // the IO thread, and only runs after this AsyncChannel is done with
75 : // the Transport.
76 : template<>
77 : struct RunnableMethodTraits<mozilla::ipc::AsyncChannel::Transport>
78 0 : {
79 0 : static void RetainCallee(mozilla::ipc::AsyncChannel::Transport* obj) { }
80 0 : static void ReleaseCallee(mozilla::ipc::AsyncChannel::Transport* obj) { }
81 : };
82 :
83 : namespace {
84 :
85 : // This is an async message
86 : class GoodbyeMessage : public IPC::Message
87 0 : {
88 : public:
89 : enum { ID = GOODBYE_MESSAGE_TYPE };
90 0 : GoodbyeMessage() :
91 0 : IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL)
92 : {
93 0 : }
94 : // XXX not much point in implementing this; maybe could help with
95 : // debugging?
96 : static bool Read(const Message* msg)
97 : {
98 : return true;
99 : }
100 : void Log(const std::string& aPrefix,
101 : FILE* aOutf) const
102 : {
103 : fputs("(special `Goodbye' message)", aOutf);
104 : }
105 : };
106 :
107 : } // namespace <anon>
108 :
109 : namespace mozilla {
110 : namespace ipc {
111 :
112 0 : AsyncChannel::Link::Link(AsyncChannel *aChan)
113 0 : : mChan(aChan)
114 : {
115 0 : }
116 :
117 0 : AsyncChannel::Link::~Link()
118 : {
119 0 : mChan = 0;
120 0 : }
121 :
122 0 : AsyncChannel::ProcessLink::ProcessLink(AsyncChannel *aChan)
123 : : Link(aChan)
124 0 : , mExistingListener(NULL)
125 : {
126 0 : }
127 :
128 0 : AsyncChannel::ProcessLink::~ProcessLink()
129 : {
130 0 : mIOLoop = 0;
131 0 : if (mTransport) {
132 0 : mTransport->set_listener(0);
133 :
134 : // we only hold a weak ref to the transport, which is "owned"
135 : // by GeckoChildProcess/GeckoThread
136 0 : mTransport = 0;
137 : }
138 0 : }
139 :
140 : void
141 0 : AsyncChannel::ProcessLink::Open(mozilla::ipc::Transport* aTransport,
142 : MessageLoop *aIOLoop,
143 : Side aSide)
144 : {
145 0 : NS_PRECONDITION(aTransport, "need transport layer");
146 :
147 : // FIXME need to check for valid channel
148 :
149 0 : mTransport = aTransport;
150 0 : mExistingListener = mTransport->set_listener(this);
151 :
152 : // FIXME figure out whether we're in parent or child, grab IO loop
153 : // appropriately
154 0 : bool needOpen = true;
155 0 : if(aIOLoop) {
156 : // We're a child or using the new arguments. Either way, we
157 : // need an open.
158 0 : needOpen = true;
159 0 : mChan->mChild = (aSide == AsyncChannel::Unknown) || (aSide == AsyncChannel::Child);
160 : } else {
161 0 : NS_PRECONDITION(aSide == Unknown, "expected default side arg");
162 :
163 : // parent
164 0 : mChan->mChild = false;
165 0 : needOpen = false;
166 0 : aIOLoop = XRE_GetIOMessageLoop();
167 : // FIXME assuming that the parent waits for the OnConnected event.
168 : // FIXME see GeckoChildProcessHost.cpp. bad assumption!
169 0 : mChan->mChannelState = ChannelConnected;
170 : }
171 :
172 0 : mIOLoop = aIOLoop;
173 :
174 0 : NS_ASSERTION(mIOLoop, "need an IO loop");
175 0 : NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
176 :
177 0 : if (needOpen) { // child process
178 0 : MonitorAutoLock lock(*mChan->mMonitor);
179 :
180 : mIOLoop->PostTask(FROM_HERE,
181 0 : NewRunnableMethod(this, &ProcessLink::OnChannelOpened));
182 :
183 : // FIXME/cjones: handle errors
184 0 : while (mChan->mChannelState != ChannelConnected) {
185 0 : mChan->mMonitor->Wait();
186 : }
187 : }
188 0 : }
189 :
190 : void
191 0 : AsyncChannel::ProcessLink::EchoMessage(Message *msg)
192 : {
193 0 : mChan->AssertWorkerThread();
194 0 : mChan->mMonitor->AssertCurrentThreadOwns();
195 :
196 : // NB: Go through this OnMessageReceived indirection so that
197 : // echoing this message does the right thing for SyncChannel
198 : // and RPCChannel too
199 : mIOLoop->PostTask(
200 : FROM_HERE,
201 0 : NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg));
202 : // OnEchoMessage takes ownership of |msg|
203 0 : }
204 :
205 : void
206 0 : AsyncChannel::ProcessLink::SendMessage(Message *msg)
207 : {
208 0 : mChan->AssertWorkerThread();
209 0 : mChan->mMonitor->AssertCurrentThreadOwns();
210 :
211 : mIOLoop->PostTask(
212 : FROM_HERE,
213 0 : NewRunnableMethod(mTransport, &Transport::Send, msg));
214 0 : }
215 :
216 : void
217 0 : AsyncChannel::ProcessLink::SendClose()
218 : {
219 0 : mChan->AssertWorkerThread();
220 0 : mChan->mMonitor->AssertCurrentThreadOwns();
221 :
222 : mIOLoop->PostTask(
223 0 : FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnCloseChannel));
224 0 : }
225 :
226 0 : AsyncChannel::ThreadLink::ThreadLink(AsyncChannel *aChan,
227 : AsyncChannel *aTargetChan)
228 : : Link(aChan),
229 0 : mTargetChan(aTargetChan)
230 : {
231 0 : }
232 :
233 0 : AsyncChannel::ThreadLink::~ThreadLink()
234 : {
235 0 : mTargetChan = 0;
236 0 : }
237 :
238 : void
239 0 : AsyncChannel::ThreadLink::EchoMessage(Message *msg)
240 : {
241 0 : mChan->AssertWorkerThread();
242 0 : mChan->mMonitor->AssertCurrentThreadOwns();
243 :
244 0 : mChan->OnMessageReceivedFromLink(*msg);
245 0 : delete msg;
246 0 : }
247 :
248 : void
249 0 : AsyncChannel::ThreadLink::SendMessage(Message *msg)
250 : {
251 0 : mChan->AssertWorkerThread();
252 0 : mChan->mMonitor->AssertCurrentThreadOwns();
253 :
254 0 : mTargetChan->OnMessageReceivedFromLink(*msg);
255 0 : delete msg;
256 0 : }
257 :
258 : void
259 0 : AsyncChannel::ThreadLink::SendClose()
260 : {
261 0 : mChan->AssertWorkerThread();
262 0 : mChan->mMonitor->AssertCurrentThreadOwns();
263 :
264 0 : mChan->mChannelState = ChannelClosed;
265 :
266 : // In a ProcessLink, we would close our half the channel. This
267 : // would show up on the other side as an error on the I/O thread.
268 : // The I/O thread would then invoke OnChannelErrorFromLink().
269 : // As usual, we skip that process and just invoke the
270 : // OnChannelErrorFromLink() method directly.
271 0 : mTargetChan->OnChannelErrorFromLink();
272 0 : }
273 :
274 1 : AsyncChannel::AsyncChannel(AsyncListener* aListener)
275 : : mListener(aListener),
276 : mChannelState(ChannelClosed),
277 : mWorkerLoop(),
278 : mChild(false),
279 : mChannelErrorTask(NULL),
280 1 : mLink(NULL)
281 : {
282 1 : MOZ_COUNT_CTOR(AsyncChannel);
283 1 : }
284 :
285 0 : AsyncChannel::~AsyncChannel()
286 : {
287 0 : MOZ_COUNT_DTOR(AsyncChannel);
288 0 : Clear();
289 0 : }
290 :
291 : bool
292 0 : AsyncChannel::Open(Transport* aTransport,
293 : MessageLoop* aIOLoop,
294 : AsyncChannel::Side aSide)
295 : {
296 : ProcessLink *link;
297 0 : NS_PRECONDITION(!mLink, "Open() called > once");
298 0 : mMonitor = new RefCountedMonitor();
299 0 : mWorkerLoop = MessageLoop::current();
300 0 : mLink = link = new ProcessLink(this);
301 0 : link->Open(aTransport, aIOLoop, aSide); // n.b.: sets mChild
302 0 : return true;
303 : }
304 :
305 : /* Opens a connection to another thread in the same process.
306 :
307 : This handshake proceeds as follows:
308 : - Let A be the thread initiating the process (either child or parent)
309 : and B be the other thread.
310 : - A spawns thread for B, obtaining B's message loop
311 : - A creates ProtocolChild and ProtocolParent instances.
312 : Let PA be the one appropriate to A and PB the side for B.
313 : - A invokes PA->Open(PB, ...):
314 : - set state to mChannelOpening
315 : - this will place a work item in B's worker loop (see next bullet)
316 : and then spins until PB->mChannelState becomes mChannelConnected
317 : - meanwhile, on PB's worker loop, the work item is removed and:
318 : - invokes PB->SlaveOpen(PA, ...):
319 : - sets its state and that of PA to Connected
320 : */
321 : bool
322 0 : AsyncChannel::Open(AsyncChannel *aTargetChan,
323 : MessageLoop *aTargetLoop,
324 : AsyncChannel::Side aSide)
325 : {
326 0 : NS_PRECONDITION(aTargetChan, "Need a target channel");
327 0 : NS_PRECONDITION(ChannelClosed == mChannelState, "Not currently closed");
328 :
329 0 : CommonThreadOpenInit(aTargetChan, aSide);
330 :
331 0 : Side oppSide = Unknown;
332 0 : switch(aSide) {
333 0 : case Child: oppSide = Parent; break;
334 0 : case Parent: oppSide = Child; break;
335 0 : case Unknown: break;
336 : }
337 :
338 0 : mMonitor = new RefCountedMonitor();
339 :
340 : aTargetLoop->PostTask(
341 : FROM_HERE,
342 : NewRunnableMethod(aTargetChan, &AsyncChannel::OnOpenAsSlave,
343 0 : this, oppSide));
344 :
345 0 : MonitorAutoLock lock(*mMonitor);
346 0 : mChannelState = ChannelOpening;
347 0 : while (ChannelOpening == mChannelState)
348 0 : mMonitor->Wait();
349 0 : NS_ASSERTION(ChannelConnected == mChannelState, "not connected when awoken");
350 0 : return (ChannelConnected == mChannelState);
351 : }
352 :
353 : void
354 0 : AsyncChannel::CommonThreadOpenInit(AsyncChannel *aTargetChan, Side aSide)
355 : {
356 0 : mWorkerLoop = MessageLoop::current();
357 0 : mLink = new ThreadLink(this, aTargetChan);
358 0 : mChild = (aSide == Child);
359 0 : }
360 :
361 : // Invoked when the other side has begun the open.
362 : void
363 0 : AsyncChannel::OnOpenAsSlave(AsyncChannel *aTargetChan, Side aSide)
364 : {
365 0 : NS_PRECONDITION(ChannelClosed == mChannelState,
366 : "Not currently closed");
367 0 : NS_PRECONDITION(ChannelOpening == aTargetChan->mChannelState,
368 : "Target channel not in the process of opening");
369 :
370 0 : CommonThreadOpenInit(aTargetChan, aSide);
371 0 : mMonitor = aTargetChan->mMonitor;
372 :
373 0 : MonitorAutoLock lock(*mMonitor);
374 0 : NS_ASSERTION(ChannelOpening == aTargetChan->mChannelState,
375 : "Target channel not in the process of opening");
376 0 : mChannelState = ChannelConnected;
377 0 : aTargetChan->mChannelState = ChannelConnected;
378 0 : aTargetChan->mMonitor->Notify();
379 0 : }
380 :
381 : void
382 0 : AsyncChannel::Close()
383 : {
384 0 : AssertWorkerThread();
385 :
386 : {
387 : // n.b.: We increase the ref count of monitor temporarily
388 : // for the duration of this block. Otherwise, the
389 : // function NotifyMaybeChannelError() will call
390 : // ::Clear() which can free the monitor.
391 0 : nsRefPtr<RefCountedMonitor> monitor(mMonitor);
392 0 : MonitorAutoLock lock(*monitor);
393 :
394 0 : if (ChannelError == mChannelState ||
395 : ChannelTimeout == mChannelState) {
396 : // See bug 538586: if the listener gets deleted while the
397 : // IO thread's NotifyChannelError event is still enqueued
398 : // and subsequently deletes us, then the error event will
399 : // also be deleted and the listener will never be notified
400 : // of the channel error.
401 0 : if (mListener) {
402 0 : MonitorAutoUnlock unlock(*monitor);
403 0 : NotifyMaybeChannelError();
404 : }
405 : return;
406 : }
407 :
408 0 : if (ChannelConnected != mChannelState)
409 : // XXX be strict about this until there's a compelling reason
410 : // to relax
411 0 : NS_RUNTIMEABORT("Close() called on closed channel!");
412 :
413 0 : AssertWorkerThread();
414 :
415 : // notify the other side that we're about to close our socket
416 0 : SendSpecialMessage(new GoodbyeMessage());
417 :
418 0 : SynchronouslyClose();
419 : }
420 :
421 0 : NotifyChannelClosed();
422 : }
423 :
424 : void
425 0 : AsyncChannel::SynchronouslyClose()
426 : {
427 0 : AssertWorkerThread();
428 0 : mMonitor->AssertCurrentThreadOwns();
429 0 : mLink->SendClose();
430 0 : while (ChannelClosed != mChannelState)
431 0 : mMonitor->Wait();
432 0 : }
433 :
434 : bool
435 0 : AsyncChannel::Send(Message* _msg)
436 : {
437 0 : nsAutoPtr<Message> msg(_msg);
438 0 : AssertWorkerThread();
439 0 : mMonitor->AssertNotCurrentThreadOwns();
440 0 : NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
441 :
442 : {
443 0 : MonitorAutoLock lock(*mMonitor);
444 :
445 0 : if (!Connected()) {
446 0 : ReportConnectionError("AsyncChannel");
447 0 : return false;
448 : }
449 :
450 0 : mLink->SendMessage(msg.forget());
451 : }
452 :
453 0 : return true;
454 : }
455 :
456 : bool
457 0 : AsyncChannel::Echo(Message* _msg)
458 : {
459 0 : nsAutoPtr<Message> msg(_msg);
460 0 : AssertWorkerThread();
461 0 : mMonitor->AssertNotCurrentThreadOwns();
462 0 : NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
463 :
464 : {
465 0 : MonitorAutoLock lock(*mMonitor);
466 :
467 0 : if (!Connected()) {
468 0 : ReportConnectionError("AsyncChannel");
469 0 : return false;
470 : }
471 :
472 0 : mLink->EchoMessage(msg.forget());
473 : }
474 :
475 0 : return true;
476 : }
477 :
478 : void
479 0 : AsyncChannel::OnDispatchMessage(const Message& msg)
480 : {
481 0 : AssertWorkerThread();
482 0 : NS_ASSERTION(!msg.is_reply(), "can't process replies here");
483 0 : NS_ASSERTION(!(msg.is_sync() || msg.is_rpc()), "async dispatch only");
484 :
485 0 : if (MSG_ROUTING_NONE == msg.routing_id()) {
486 0 : if (!OnSpecialMessage(msg.type(), msg))
487 : // XXX real error handling
488 0 : NS_RUNTIMEABORT("unhandled special message!");
489 0 : return;
490 : }
491 :
492 : // it's OK to dispatch messages if the channel is closed/error'd,
493 : // since we don't have a reply to send back
494 :
495 0 : (void)MaybeHandleError(mListener->OnMessageReceived(msg), "AsyncChannel");
496 : }
497 :
498 : bool
499 0 : AsyncChannel::OnSpecialMessage(uint16 id, const Message& msg)
500 : {
501 0 : return false;
502 : }
503 :
504 : void
505 0 : AsyncChannel::SendSpecialMessage(Message* msg) const
506 : {
507 0 : AssertWorkerThread();
508 0 : mLink->SendMessage(msg);
509 0 : }
510 :
511 : void
512 0 : AsyncChannel::OnNotifyMaybeChannelError()
513 : {
514 0 : AssertWorkerThread();
515 0 : mMonitor->AssertNotCurrentThreadOwns();
516 :
517 : // OnChannelError holds mMonitor when it posts this task and this
518 : // task cannot be allowed to run until OnChannelError has
519 : // exited. We enforce that order by grabbing the mutex here which
520 : // should only continue once OnChannelError has completed.
521 : {
522 0 : MonitorAutoLock lock(*mMonitor);
523 : // nothing to do here
524 : }
525 :
526 0 : if (ShouldDeferNotifyMaybeError()) {
527 : mChannelErrorTask =
528 0 : NewRunnableMethod(this, &AsyncChannel::OnNotifyMaybeChannelError);
529 : // 10 ms delay is completely arbitrary
530 0 : mWorkerLoop->PostDelayedTask(FROM_HERE, mChannelErrorTask, 10);
531 0 : return;
532 : }
533 :
534 0 : NotifyMaybeChannelError();
535 : }
536 :
537 : void
538 0 : AsyncChannel::NotifyChannelClosed()
539 : {
540 0 : mMonitor->AssertNotCurrentThreadOwns();
541 :
542 0 : if (ChannelClosed != mChannelState)
543 0 : NS_RUNTIMEABORT("channel should have been closed!");
544 :
545 : // OK, the IO thread just closed the channel normally. Let the
546 : // listener know about it.
547 0 : mListener->OnChannelClose();
548 :
549 0 : Clear();
550 0 : }
551 :
552 : void
553 0 : AsyncChannel::NotifyMaybeChannelError()
554 : {
555 0 : mMonitor->AssertNotCurrentThreadOwns();
556 :
557 : // TODO sort out Close() on this side racing with Close() on the
558 : // other side
559 0 : if (ChannelClosing == mChannelState) {
560 : // the channel closed, but we received a "Goodbye" message
561 : // warning us about it. no worries
562 0 : mChannelState = ChannelClosed;
563 0 : NotifyChannelClosed();
564 0 : return;
565 : }
566 :
567 : // Oops, error! Let the listener know about it.
568 0 : mChannelState = ChannelError;
569 0 : mListener->OnChannelError();
570 :
571 0 : Clear();
572 : }
573 :
574 : void
575 0 : AsyncChannel::Clear()
576 : {
577 0 : mListener = 0;
578 0 : mWorkerLoop = 0;
579 :
580 0 : delete mLink;
581 0 : mLink = 0;
582 0 : mMonitor = 0;
583 :
584 0 : if (mChannelErrorTask) {
585 0 : mChannelErrorTask->Cancel();
586 0 : mChannelErrorTask = NULL;
587 : }
588 0 : }
589 :
590 : static void
591 0 : PrintErrorMessage(bool isChild, const char* channelName, const char* msg)
592 : {
593 : #ifdef DEBUG
594 : fprintf(stderr, "\n###!!! [%s][%s] Error: %s\n\n",
595 0 : isChild ? "Child" : "Parent", channelName, msg);
596 : #endif
597 0 : }
598 :
599 : bool
600 0 : AsyncChannel::MaybeHandleError(Result code, const char* channelName)
601 : {
602 0 : if (MsgProcessed == code)
603 0 : return true;
604 :
605 : const char* errorMsg;
606 0 : switch (code) {
607 : case MsgNotKnown:
608 0 : errorMsg = "Unknown message: not processed";
609 0 : break;
610 : case MsgNotAllowed:
611 0 : errorMsg = "Message not allowed: cannot be sent/recvd in this state";
612 0 : break;
613 : case MsgPayloadError:
614 0 : errorMsg = "Payload error: message could not be deserialized";
615 0 : break;
616 : case MsgProcessingError:
617 0 : errorMsg = "Processing error: message was deserialized, but the handler returned false (indicating failure)";
618 0 : break;
619 : case MsgRouteError:
620 0 : errorMsg = "Route error: message sent to unknown actor ID";
621 0 : break;
622 : case MsgValueError:
623 0 : errorMsg = "Value error: message was deserialized, but contained an illegal value";
624 0 : break;
625 :
626 : default:
627 0 : NS_RUNTIMEABORT("unknown Result code");
628 0 : return false;
629 : }
630 :
631 0 : PrintErrorMessage(mChild, channelName, errorMsg);
632 :
633 0 : mListener->OnProcessingError(code);
634 :
635 0 : return false;
636 : }
637 :
638 : void
639 0 : AsyncChannel::ReportConnectionError(const char* channelName) const
640 : {
641 : const char* errorMsg;
642 0 : switch (mChannelState) {
643 : case ChannelClosed:
644 0 : errorMsg = "Closed channel: cannot send/recv";
645 0 : break;
646 : case ChannelOpening:
647 0 : errorMsg = "Opening channel: not yet ready for send/recv";
648 0 : break;
649 : case ChannelTimeout:
650 0 : errorMsg = "Channel timeout: cannot send/recv";
651 0 : break;
652 : case ChannelClosing:
653 0 : errorMsg = "Channel closing: too late to send/recv, messages will be lost";
654 0 : break;
655 : case ChannelError:
656 0 : errorMsg = "Channel error: cannot send/recv";
657 0 : break;
658 :
659 : default:
660 0 : NS_RUNTIMEABORT("unreached");
661 : }
662 :
663 0 : PrintErrorMessage(mChild, channelName, errorMsg);
664 :
665 0 : mListener->OnProcessingError(MsgDropped);
666 0 : }
667 :
668 : void
669 0 : AsyncChannel::DispatchOnChannelConnected(int32 peer_pid)
670 : {
671 0 : AssertWorkerThread();
672 0 : if (mListener)
673 0 : mListener->OnChannelConnected(peer_pid);
674 0 : }
675 :
676 : //
677 : // The methods below run in the context of the IO thread
678 : //
679 :
680 : void
681 0 : AsyncChannel::ProcessLink::OnMessageReceived(const Message& msg)
682 : {
683 0 : AssertIOThread();
684 0 : NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
685 0 : MonitorAutoLock lock(*mChan->mMonitor);
686 0 : mChan->OnMessageReceivedFromLink(msg);
687 0 : }
688 :
689 : void
690 0 : AsyncChannel::ProcessLink::OnEchoMessage(Message* msg)
691 : {
692 0 : AssertIOThread();
693 0 : OnMessageReceived(*msg);
694 0 : delete msg;
695 0 : }
696 :
697 : void
698 0 : AsyncChannel::ProcessLink::OnChannelOpened()
699 : {
700 0 : mChan->AssertLinkThread();
701 : {
702 0 : MonitorAutoLock lock(*mChan->mMonitor);
703 0 : mChan->mChannelState = ChannelOpening;
704 : }
705 0 : /*assert*/mTransport->Connect();
706 0 : }
707 :
708 : void
709 0 : AsyncChannel::ProcessLink::OnChannelConnected(int32 peer_pid)
710 : {
711 0 : AssertIOThread();
712 :
713 : {
714 0 : MonitorAutoLock lock(*mChan->mMonitor);
715 0 : mChan->mChannelState = ChannelConnected;
716 0 : mChan->mMonitor->Notify();
717 : }
718 :
719 0 : if(mExistingListener)
720 0 : mExistingListener->OnChannelConnected(peer_pid);
721 :
722 : mChan->mWorkerLoop->PostTask(
723 : FROM_HERE,
724 : NewRunnableMethod(mChan,
725 : &AsyncChannel::DispatchOnChannelConnected,
726 0 : peer_pid));
727 0 : }
728 :
729 : void
730 0 : AsyncChannel::ProcessLink::OnChannelError()
731 : {
732 0 : AssertIOThread();
733 0 : MonitorAutoLock lock(*mChan->mMonitor);
734 0 : mChan->OnChannelErrorFromLink();
735 0 : }
736 :
737 : void
738 0 : AsyncChannel::ProcessLink::OnCloseChannel()
739 : {
740 0 : AssertIOThread();
741 :
742 0 : mTransport->Close();
743 :
744 0 : MonitorAutoLock lock(*mChan->mMonitor);
745 0 : mChan->mChannelState = ChannelClosed;
746 0 : mChan->mMonitor->Notify();
747 0 : }
748 :
749 : //
750 : // The methods below run in the context of the link thread
751 : //
752 :
753 : void
754 0 : AsyncChannel::OnMessageReceivedFromLink(const Message& msg)
755 : {
756 0 : AssertLinkThread();
757 0 : mMonitor->AssertCurrentThreadOwns();
758 :
759 0 : if (!MaybeInterceptSpecialIOMessage(msg))
760 : // wake up the worker, there's work to do
761 : mWorkerLoop->PostTask(
762 : FROM_HERE,
763 0 : NewRunnableMethod(this, &AsyncChannel::OnDispatchMessage, msg));
764 0 : }
765 :
766 : void
767 0 : AsyncChannel::OnChannelErrorFromLink()
768 : {
769 0 : AssertLinkThread();
770 0 : mMonitor->AssertCurrentThreadOwns();
771 :
772 0 : if (ChannelClosing != mChannelState)
773 0 : mChannelState = ChannelError;
774 :
775 0 : PostErrorNotifyTask();
776 0 : }
777 :
778 : void
779 0 : AsyncChannel::PostErrorNotifyTask()
780 : {
781 0 : AssertLinkThread();
782 0 : mMonitor->AssertCurrentThreadOwns();
783 :
784 0 : NS_ASSERTION(!mChannelErrorTask, "OnChannelError called twice?");
785 :
786 : // This must be the last code that runs on this thread!
787 : mChannelErrorTask =
788 0 : NewRunnableMethod(this, &AsyncChannel::OnNotifyMaybeChannelError);
789 0 : mWorkerLoop->PostTask(FROM_HERE, mChannelErrorTask);
790 0 : }
791 :
792 : bool
793 0 : AsyncChannel::MaybeInterceptSpecialIOMessage(const Message& msg)
794 : {
795 0 : AssertLinkThread();
796 0 : mMonitor->AssertCurrentThreadOwns();
797 :
798 0 : if (MSG_ROUTING_NONE == msg.routing_id()
799 0 : && GOODBYE_MESSAGE_TYPE == msg.type()) {
800 0 : ProcessGoodbyeMessage();
801 0 : return true;
802 : }
803 0 : return false;
804 : }
805 :
806 : void
807 0 : AsyncChannel::ProcessGoodbyeMessage()
808 : {
809 0 : AssertLinkThread();
810 0 : mMonitor->AssertCurrentThreadOwns();
811 :
812 : // TODO sort out Close() on this side racing with Close() on the
813 : // other side
814 0 : mChannelState = ChannelClosing;
815 :
816 : printf("NOTE: %s process received `Goodbye', closing down\n",
817 0 : mChild ? "child" : "parent");
818 0 : }
819 :
820 :
821 : } // namespace ipc
822 : } // namespace mozilla
|