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/RPCChannel.h"
41 : #include "mozilla/ipc/ProtocolUtils.h"
42 :
43 : #include "nsDebug.h"
44 : #include "nsTraceRefcnt.h"
45 :
46 : #define RPC_ASSERT(_cond, ...) \
47 : do { \
48 : if (!(_cond)) \
49 : DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__); \
50 : } while (0)
51 :
52 : using mozilla::MonitorAutoLock;
53 : using mozilla::MonitorAutoUnlock;
54 :
55 : template<>
56 : struct RunnableMethodTraits<mozilla::ipc::RPCChannel>
57 1 : {
58 1 : static void RetainCallee(mozilla::ipc::RPCChannel* obj) { }
59 0 : static void ReleaseCallee(mozilla::ipc::RPCChannel* obj) { }
60 : };
61 :
62 :
63 : namespace
64 : {
65 :
66 : // Async (from the sending side's perspective)
67 : class BlockChildMessage : public IPC::Message
68 0 : {
69 : public:
70 : enum { ID = BLOCK_CHILD_MESSAGE_TYPE };
71 0 : BlockChildMessage() :
72 0 : Message(MSG_ROUTING_NONE, ID, IPC::Message::PRIORITY_NORMAL)
73 0 : { }
74 : };
75 :
76 : // Async
77 : class UnblockChildMessage : public IPC::Message
78 0 : {
79 : public:
80 : enum { ID = UNBLOCK_CHILD_MESSAGE_TYPE };
81 0 : UnblockChildMessage() :
82 0 : Message(MSG_ROUTING_NONE, ID, IPC::Message::PRIORITY_NORMAL)
83 0 : { }
84 : };
85 :
86 : } // namespace <anon>
87 :
88 :
89 : namespace mozilla {
90 : namespace ipc {
91 :
92 1 : RPCChannel::RPCChannel(RPCListener* aListener)
93 : : SyncChannel(aListener),
94 : mPending(),
95 : mStack(),
96 : mOutOfTurnReplies(),
97 : mDeferred(),
98 : mRemoteStackDepthGuess(0),
99 : mBlockedOnParent(false),
100 1 : mSawRPCOutMsg(false)
101 : {
102 1 : MOZ_COUNT_CTOR(RPCChannel);
103 :
104 : mDequeueOneTask = new RefCountedTask(NewRunnableMethod(
105 : this,
106 2 : &RPCChannel::OnMaybeDequeueOne));
107 1 : }
108 :
109 0 : RPCChannel::~RPCChannel()
110 : {
111 0 : MOZ_COUNT_DTOR(RPCChannel);
112 0 : RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
113 0 : }
114 :
115 : void
116 0 : RPCChannel::Clear()
117 : {
118 0 : mDequeueOneTask->Cancel();
119 :
120 0 : AsyncChannel::Clear();
121 0 : }
122 :
123 : bool
124 0 : RPCChannel::EventOccurred() const
125 : {
126 0 : AssertWorkerThread();
127 0 : mMonitor->AssertCurrentThreadOwns();
128 0 : RPC_ASSERT(StackDepth() > 0, "not in wait loop");
129 :
130 0 : return (!Connected() ||
131 0 : !mPending.empty() ||
132 0 : (!mOutOfTurnReplies.empty() &&
133 0 : mOutOfTurnReplies.find(mStack.top().seqno())
134 0 : != mOutOfTurnReplies.end()));
135 : }
136 :
137 : bool
138 0 : RPCChannel::Send(Message* msg)
139 : {
140 0 : Message copy = *msg;
141 0 : CxxStackFrame f(*this, OUT_MESSAGE, ©);
142 0 : return AsyncChannel::Send(msg);
143 : }
144 :
145 : bool
146 0 : RPCChannel::Send(Message* msg, Message* reply)
147 : {
148 0 : Message copy = *msg;
149 0 : CxxStackFrame f(*this, OUT_MESSAGE, ©);
150 0 : return SyncChannel::Send(msg, reply);
151 : }
152 :
153 : bool
154 0 : RPCChannel::Call(Message* _msg, Message* reply)
155 : {
156 0 : nsAutoPtr<Message> msg(_msg);
157 0 : AssertWorkerThread();
158 0 : mMonitor->AssertNotCurrentThreadOwns();
159 0 : RPC_ASSERT(!ProcessingSyncMessage(),
160 : "violation of sync handler invariant");
161 0 : RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here");
162 :
163 : #ifdef OS_WIN
164 : SyncStackFrame frame(this, true);
165 : #endif
166 :
167 0 : Message copy = *msg;
168 0 : CxxStackFrame f(*this, OUT_MESSAGE, ©);
169 :
170 0 : MonitorAutoLock lock(*mMonitor);
171 :
172 0 : if (!Connected()) {
173 0 : ReportConnectionError("RPCChannel");
174 0 : return false;
175 : }
176 :
177 0 : msg->set_seqno(NextSeqno());
178 0 : msg->set_rpc_remote_stack_depth_guess(mRemoteStackDepthGuess);
179 0 : msg->set_rpc_local_stack_depth(1 + StackDepth());
180 0 : mStack.push(*msg);
181 :
182 0 : mLink->SendMessage(msg.forget());
183 :
184 0 : while (1) {
185 : // if a handler invoked by *Dispatch*() spun a nested event
186 : // loop, and the connection was broken during that loop, we
187 : // might have already processed the OnError event. if so,
188 : // trying another loop iteration will be futile because
189 : // channel state will have been cleared
190 0 : if (!Connected()) {
191 0 : ReportConnectionError("RPCChannel");
192 0 : return false;
193 : }
194 :
195 : // now might be the time to process a message deferred because
196 : // of race resolution
197 0 : MaybeUndeferIncall();
198 :
199 : // here we're waiting for something to happen. see long
200 : // comment about the queue in RPCChannel.h
201 0 : while (!EventOccurred()) {
202 0 : bool maybeTimedOut = !RPCChannel::WaitForNotify();
203 :
204 0 : if (EventOccurred() ||
205 : // we might have received a "subtly deferred" message
206 : // in a nested loop that it's now time to process
207 0 : (!maybeTimedOut &&
208 0 : (!mDeferred.empty() || !mOutOfTurnReplies.empty())))
209 0 : break;
210 :
211 0 : if (maybeTimedOut && !ShouldContinueFromTimeout())
212 0 : return false;
213 : }
214 :
215 0 : if (!Connected()) {
216 0 : ReportConnectionError("RPCChannel");
217 0 : return false;
218 : }
219 :
220 0 : Message recvd;
221 0 : MessageMap::iterator it;
222 0 : if (!mOutOfTurnReplies.empty() &&
223 0 : ((it = mOutOfTurnReplies.find(mStack.top().seqno())) !=
224 0 : mOutOfTurnReplies.end())) {
225 0 : recvd = it->second;
226 0 : mOutOfTurnReplies.erase(it);
227 : }
228 0 : else if (!mPending.empty()) {
229 0 : recvd = mPending.front();
230 0 : mPending.pop();
231 : }
232 : else {
233 : // because of subtleties with nested event loops, it's
234 : // possible that we got here and nothing happened. or, we
235 : // might have a deferred in-call that needs to be
236 : // processed. either way, we won't break the inner while
237 : // loop again until something new happens.
238 0 : continue;
239 : }
240 :
241 0 : if (!recvd.is_sync() && !recvd.is_rpc()) {
242 0 : MonitorAutoUnlock unlock(*mMonitor);
243 :
244 0 : CxxStackFrame f(*this, IN_MESSAGE, &recvd);
245 0 : AsyncChannel::OnDispatchMessage(recvd);
246 :
247 0 : continue;
248 : }
249 :
250 0 : if (recvd.is_sync()) {
251 0 : RPC_ASSERT(mPending.empty(),
252 : "other side should have been blocked");
253 0 : MonitorAutoUnlock unlock(*mMonitor);
254 :
255 0 : CxxStackFrame f(*this, IN_MESSAGE, &recvd);
256 0 : SyncChannel::OnDispatchMessage(recvd);
257 :
258 0 : continue;
259 : }
260 :
261 0 : RPC_ASSERT(recvd.is_rpc(), "wtf???");
262 :
263 0 : if (recvd.is_reply()) {
264 0 : RPC_ASSERT(0 < mStack.size(), "invalid RPC stack");
265 :
266 0 : const Message& outcall = mStack.top();
267 :
268 : // in the parent, seqno's increase from 0, and in the
269 : // child, they decrease from 0
270 0 : if ((!mChild && recvd.seqno() < outcall.seqno()) ||
271 0 : (mChild && recvd.seqno() > outcall.seqno())) {
272 0 : mOutOfTurnReplies[recvd.seqno()] = recvd;
273 0 : continue;
274 : }
275 :
276 : // FIXME/cjones: handle error
277 0 : RPC_ASSERT(
278 : recvd.is_reply_error() ||
279 : (recvd.type() == (outcall.type()+1) &&
280 : recvd.seqno() == outcall.seqno()),
281 : "somebody's misbehavin'", "rpc", true);
282 :
283 : // we received a reply to our most recent outstanding
284 : // call. pop this frame and return the reply
285 0 : mStack.pop();
286 :
287 0 : bool isError = recvd.is_reply_error();
288 0 : if (!isError) {
289 0 : *reply = recvd;
290 : }
291 :
292 0 : if (0 == StackDepth()) {
293 0 : RPC_ASSERT(
294 : mOutOfTurnReplies.empty(),
295 : "still have pending replies with no pending out-calls",
296 : "rpc", true);
297 : }
298 :
299 : // finished with this RPC stack frame
300 0 : return !isError;
301 : }
302 :
303 : // in-call. process in a new stack frame.
304 :
305 : // "snapshot" the current stack depth while we own the Monitor
306 0 : size_t stackDepth = StackDepth();
307 : {
308 0 : MonitorAutoUnlock unlock(*mMonitor);
309 : // someone called in to us from the other side. handle the call
310 0 : CxxStackFrame f(*this, IN_MESSAGE, &recvd);
311 0 : Incall(recvd, stackDepth);
312 : // FIXME/cjones: error handling
313 : }
314 : }
315 :
316 : return true;
317 : }
318 :
319 : void
320 0 : RPCChannel::MaybeUndeferIncall()
321 : {
322 0 : AssertWorkerThread();
323 0 : mMonitor->AssertCurrentThreadOwns();
324 :
325 0 : if (mDeferred.empty())
326 0 : return;
327 :
328 0 : size_t stackDepth = StackDepth();
329 :
330 : // the other side can only *under*-estimate our actual stack depth
331 0 : RPC_ASSERT(mDeferred.top().rpc_remote_stack_depth_guess() <= stackDepth,
332 : "fatal logic error");
333 :
334 0 : if (mDeferred.top().rpc_remote_stack_depth_guess() < RemoteViewOfStackDepth(stackDepth))
335 0 : return;
336 :
337 : // maybe time to process this message
338 0 : Message call = mDeferred.top();
339 0 : mDeferred.pop();
340 :
341 : // fix up fudge factor we added to account for race
342 0 : RPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
343 0 : --mRemoteStackDepthGuess;
344 :
345 0 : mPending.push(call);
346 : }
347 :
348 : void
349 0 : RPCChannel::EnqueuePendingMessages()
350 : {
351 0 : AssertWorkerThread();
352 0 : mMonitor->AssertCurrentThreadOwns();
353 :
354 0 : MaybeUndeferIncall();
355 :
356 0 : for (size_t i = 0; i < mDeferred.size(); ++i)
357 : mWorkerLoop->PostTask(
358 : FROM_HERE,
359 0 : new DequeueTask(mDequeueOneTask));
360 :
361 : // XXX performance tuning knob: could process all or k pending
362 : // messages here, rather than enqueuing for later processing
363 :
364 0 : for (size_t i = 0; i < mPending.size(); ++i)
365 : mWorkerLoop->PostTask(
366 : FROM_HERE,
367 0 : new DequeueTask(mDequeueOneTask));
368 0 : }
369 :
370 : void
371 0 : RPCChannel::FlushPendingRPCQueue()
372 : {
373 0 : AssertWorkerThread();
374 0 : mMonitor->AssertNotCurrentThreadOwns();
375 :
376 : {
377 0 : MonitorAutoLock lock(*mMonitor);
378 :
379 0 : if (mDeferred.empty()) {
380 0 : if (mPending.empty())
381 : return;
382 :
383 0 : const Message& last = mPending.back();
384 0 : if (!last.is_rpc() || last.is_reply())
385 : return;
386 : }
387 : }
388 :
389 0 : while (OnMaybeDequeueOne());
390 : }
391 :
392 : bool
393 0 : RPCChannel::OnMaybeDequeueOne()
394 : {
395 : // XXX performance tuning knob: could process all or k pending
396 : // messages here
397 :
398 0 : AssertWorkerThread();
399 0 : mMonitor->AssertNotCurrentThreadOwns();
400 :
401 0 : Message recvd;
402 : {
403 0 : MonitorAutoLock lock(*mMonitor);
404 :
405 0 : if (!Connected()) {
406 0 : ReportConnectionError("RPCChannel");
407 0 : return false;
408 : }
409 :
410 0 : if (!mDeferred.empty())
411 0 : MaybeUndeferIncall();
412 :
413 0 : if (mPending.empty())
414 0 : return false;
415 :
416 0 : recvd = mPending.front();
417 0 : mPending.pop();
418 : }
419 :
420 0 : if (IsOnCxxStack() && recvd.is_rpc() && recvd.is_reply()) {
421 : // We probably just received a reply in a nested loop for an
422 : // RPC call sent before entering that loop.
423 0 : mOutOfTurnReplies[recvd.seqno()] = recvd;
424 0 : return false;
425 : }
426 :
427 0 : CxxStackFrame f(*this, IN_MESSAGE, &recvd);
428 :
429 0 : if (recvd.is_rpc())
430 0 : Incall(recvd, 0);
431 0 : else if (recvd.is_sync())
432 0 : SyncChannel::OnDispatchMessage(recvd);
433 : else
434 0 : AsyncChannel::OnDispatchMessage(recvd);
435 :
436 0 : return true;
437 : }
438 :
439 : size_t
440 0 : RPCChannel::RemoteViewOfStackDepth(size_t stackDepth) const
441 : {
442 0 : AssertWorkerThread();
443 0 : return stackDepth - mOutOfTurnReplies.size();
444 : }
445 :
446 : void
447 0 : RPCChannel::Incall(const Message& call, size_t stackDepth)
448 : {
449 0 : AssertWorkerThread();
450 0 : mMonitor->AssertNotCurrentThreadOwns();
451 0 : RPC_ASSERT(call.is_rpc() && !call.is_reply(), "wrong message type");
452 :
453 : // Race detection: see the long comment near
454 : // mRemoteStackDepthGuess in RPCChannel.h. "Remote" stack depth
455 : // means our side, and "local" means other side.
456 0 : if (call.rpc_remote_stack_depth_guess() != RemoteViewOfStackDepth(stackDepth)) {
457 : // RPC in-calls have raced.
458 : // the "winner", if there is one, gets to defer processing of
459 : // the other side's in-call
460 : bool defer;
461 : const char* winner;
462 0 : switch (Listener()->MediateRPCRace(mChild ? call : mStack.top(),
463 0 : mChild ? mStack.top() : call)) {
464 : case RRPChildWins:
465 0 : winner = "child";
466 0 : defer = mChild;
467 0 : break;
468 : case RRPParentWins:
469 0 : winner = "parent";
470 0 : defer = !mChild;
471 0 : break;
472 : case RRPError:
473 0 : NS_RUNTIMEABORT("NYI: 'Error' RPC race policy");
474 0 : return;
475 : default:
476 0 : NS_RUNTIMEABORT("not reached");
477 0 : return;
478 : }
479 :
480 0 : if (LoggingEnabled()) {
481 : fprintf(stderr, " (%s: %s won, so we're%sdeferring)\n",
482 0 : mChild ? "child" : "parent", winner, defer ? " " : " not ");
483 : }
484 :
485 0 : if (defer) {
486 : // we now know the other side's stack has one more frame
487 : // than we thought
488 0 : ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
489 0 : mDeferred.push(call);
490 0 : return;
491 : }
492 :
493 : // we "lost" and need to process the other side's in-call.
494 : // don't need to fix up the mRemoteStackDepthGuess here,
495 : // because we're just about to increment it in DispatchCall(),
496 : // which will make it correct again
497 : }
498 :
499 : #ifdef OS_WIN
500 : SyncStackFrame frame(this, true);
501 : #endif
502 :
503 0 : DispatchIncall(call);
504 : }
505 :
506 : void
507 0 : RPCChannel::DispatchIncall(const Message& call)
508 : {
509 0 : AssertWorkerThread();
510 0 : mMonitor->AssertNotCurrentThreadOwns();
511 0 : RPC_ASSERT(call.is_rpc() && !call.is_reply(),
512 : "wrong message type");
513 :
514 0 : Message* reply = nsnull;
515 :
516 0 : ++mRemoteStackDepthGuess;
517 0 : Result rv = Listener()->OnCallReceived(call, reply);
518 0 : --mRemoteStackDepthGuess;
519 :
520 0 : if (!MaybeHandleError(rv, "RPCChannel")) {
521 0 : delete reply;
522 0 : reply = new Message();
523 0 : reply->set_rpc();
524 0 : reply->set_reply();
525 0 : reply->set_reply_error();
526 : }
527 :
528 0 : reply->set_seqno(call.seqno());
529 :
530 : {
531 0 : MonitorAutoLock lock(*mMonitor);
532 0 : if (ChannelConnected == mChannelState)
533 0 : mLink->SendMessage(reply);
534 : }
535 0 : }
536 :
537 : bool
538 0 : RPCChannel::BlockChild()
539 : {
540 0 : AssertWorkerThread();
541 :
542 0 : if (mChild)
543 0 : NS_RUNTIMEABORT("child tried to block parent");
544 :
545 0 : MonitorAutoLock lock(*mMonitor);
546 0 : SendSpecialMessage(new BlockChildMessage());
547 0 : return true;
548 : }
549 :
550 : bool
551 0 : RPCChannel::UnblockChild()
552 : {
553 0 : AssertWorkerThread();
554 :
555 0 : if (mChild)
556 0 : NS_RUNTIMEABORT("child tried to unblock parent");
557 :
558 0 : MonitorAutoLock lock(*mMonitor);
559 0 : SendSpecialMessage(new UnblockChildMessage());
560 0 : return true;
561 : }
562 :
563 : bool
564 0 : RPCChannel::OnSpecialMessage(uint16 id, const Message& msg)
565 : {
566 0 : AssertWorkerThread();
567 :
568 0 : switch (id) {
569 : case BLOCK_CHILD_MESSAGE_TYPE:
570 0 : BlockOnParent();
571 0 : return true;
572 :
573 : case UNBLOCK_CHILD_MESSAGE_TYPE:
574 0 : UnblockFromParent();
575 0 : return true;
576 :
577 : default:
578 0 : return SyncChannel::OnSpecialMessage(id, msg);
579 : }
580 : }
581 :
582 : void
583 0 : RPCChannel::BlockOnParent()
584 : {
585 0 : AssertWorkerThread();
586 :
587 0 : if (!mChild)
588 0 : NS_RUNTIMEABORT("child tried to block parent");
589 :
590 0 : MonitorAutoLock lock(*mMonitor);
591 :
592 0 : if (mBlockedOnParent || AwaitingSyncReply() || 0 < StackDepth())
593 0 : NS_RUNTIMEABORT("attempt to block child when it's already blocked");
594 :
595 0 : mBlockedOnParent = true;
596 0 : do {
597 : // XXX this dispatch loop shares some similarities with the
598 : // one in Call(), but the logic is simpler and different
599 : // enough IMHO to warrant its own dispatch loop
600 0 : while (Connected() && mPending.empty() && mBlockedOnParent) {
601 0 : WaitForNotify();
602 : }
603 :
604 0 : if (!Connected()) {
605 0 : mBlockedOnParent = false;
606 0 : ReportConnectionError("RPCChannel");
607 0 : break;
608 : }
609 :
610 0 : if (!mPending.empty()) {
611 0 : Message recvd = mPending.front();
612 0 : mPending.pop();
613 :
614 0 : MonitorAutoUnlock unlock(*mMonitor);
615 :
616 0 : CxxStackFrame f(*this, IN_MESSAGE, &recvd);
617 0 : if (recvd.is_rpc()) {
618 : // stack depth must be 0 here
619 0 : Incall(recvd, 0);
620 : }
621 0 : else if (recvd.is_sync()) {
622 0 : SyncChannel::OnDispatchMessage(recvd);
623 : }
624 : else {
625 0 : AsyncChannel::OnDispatchMessage(recvd);
626 : }
627 : }
628 : } while (mBlockedOnParent);
629 :
630 0 : EnqueuePendingMessages();
631 0 : }
632 :
633 : void
634 0 : RPCChannel::UnblockFromParent()
635 : {
636 0 : AssertWorkerThread();
637 :
638 0 : if (!mChild)
639 0 : NS_RUNTIMEABORT("child tried to block parent");
640 0 : MonitorAutoLock lock(*mMonitor);
641 0 : mBlockedOnParent = false;
642 0 : }
643 :
644 : void
645 0 : RPCChannel::ExitedCxxStack()
646 : {
647 0 : Listener()->OnExitedCxxStack();
648 0 : if (mSawRPCOutMsg) {
649 0 : MonitorAutoLock lock(*mMonitor);
650 : // see long comment in OnMaybeDequeueOne()
651 0 : EnqueuePendingMessages();
652 0 : mSawRPCOutMsg = false;
653 : }
654 0 : }
655 :
656 : void
657 0 : RPCChannel::DebugAbort(const char* file, int line, const char* cond,
658 : const char* why,
659 : const char* type, bool reply) const
660 : {
661 : fprintf(stderr,
662 : "###!!! [RPCChannel][%s][%s:%d] "
663 : "Assertion (%s) failed. %s (triggered by %s%s)\n",
664 : mChild ? "Child" : "Parent",
665 : file, line, cond,
666 : why,
667 0 : type, reply ? "reply" : "");
668 : // technically we need the mutex for this, but we're dying anyway
669 0 : DumpRPCStack(stderr, " ");
670 : fprintf(stderr, " remote RPC stack guess: %lu\n",
671 0 : mRemoteStackDepthGuess);
672 : fprintf(stderr, " deferred stack size: %lu\n",
673 0 : mDeferred.size());
674 : fprintf(stderr, " out-of-turn RPC replies stack size: %lu\n",
675 0 : mOutOfTurnReplies.size());
676 : fprintf(stderr, " Pending queue size: %lu, front to back:\n",
677 0 : mPending.size());
678 :
679 0 : MessageQueue pending = mPending;
680 0 : while (!pending.empty()) {
681 : fprintf(stderr, " [ %s%s ]\n",
682 0 : pending.front().is_rpc() ? "rpc" :
683 0 : (pending.front().is_sync() ? "sync" : "async"),
684 0 : pending.front().is_reply() ? "reply" : "");
685 0 : pending.pop();
686 : }
687 :
688 0 : NS_RUNTIMEABORT(why);
689 0 : }
690 :
691 : void
692 0 : RPCChannel::DumpRPCStack(FILE* outfile, const char* const pfx) const
693 : {
694 0 : NS_WARN_IF_FALSE(MessageLoop::current() != mWorkerLoop,
695 : "The worker thread had better be paused in a debugger!");
696 :
697 0 : if (!outfile)
698 0 : outfile = stdout;
699 :
700 0 : fprintf(outfile, "%sRPCChannel 'backtrace':\n", pfx);
701 :
702 : // print a python-style backtrace, first frame to last
703 0 : for (PRUint32 i = 0; i < mCxxStackFrames.size(); ++i) {
704 : int32 id;
705 : const char* dir, *sems, *name;
706 0 : mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
707 :
708 : fprintf(outfile, "%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
709 0 : i, dir, sems, name, id);
710 : }
711 0 : }
712 :
713 : //
714 : // The methods below run in the context of the link thread, and can proxy
715 : // back to the methods above
716 : //
717 :
718 : void
719 0 : RPCChannel::OnMessageReceivedFromLink(const Message& msg)
720 : {
721 0 : AssertLinkThread();
722 0 : mMonitor->AssertCurrentThreadOwns();
723 :
724 0 : if (MaybeInterceptSpecialIOMessage(msg))
725 0 : return;
726 :
727 : // regardless of the RPC stack, if we're awaiting a sync reply, we
728 : // know that it needs to be immediately handled to unblock us.
729 0 : if (AwaitingSyncReply() && msg.is_sync()) {
730 : // wake up worker thread waiting at SyncChannel::Send
731 0 : mRecvd = msg;
732 0 : NotifyWorkerThread();
733 0 : return;
734 : }
735 :
736 0 : mPending.push(msg);
737 :
738 0 : if (0 == StackDepth() && !mBlockedOnParent) {
739 : // the worker thread might be idle, make sure it wakes up
740 : mWorkerLoop->PostTask(FROM_HERE,
741 0 : new DequeueTask(mDequeueOneTask));
742 : }
743 0 : else if (!AwaitingSyncReply())
744 0 : NotifyWorkerThread();
745 : }
746 :
747 :
748 : void
749 0 : RPCChannel::OnChannelErrorFromLink()
750 : {
751 0 : AssertLinkThread();
752 0 : mMonitor->AssertCurrentThreadOwns();
753 :
754 0 : if (0 < StackDepth())
755 0 : NotifyWorkerThread();
756 :
757 0 : SyncChannel::OnChannelErrorFromLink();
758 0 : }
759 :
760 : } // namespace ipc
761 : } // namespace mozilla
762 :
|