bool SyncChannel::Send(Message* msg, Message* reply) { AssertWorkerThread(); mMonitor.AssertNotCurrentThreadOwns(); NS_ABORT_IF_FALSE(!ProcessingSyncMessage(), "violation of sync handler invariant"); NS_ABORT_IF_FALSE(msg->is_sync(), "can only Send() sync messages here"); #ifdef OS_WIN SyncStackFrame frame(this, false); #endif msg->set_seqno(NextSeqno()); MonitorAutoLock lock(mMonitor); if (!Connected()) { ReportConnectionError("SyncChannel"); return false; } mPendingReply = msg->type() + 1; int32 msgSeqno = msg->seqno(); SendThroughTransport(msg); while (1) { bool maybeTimedOut = !SyncChannel::WaitForNotify(); if (EventOccurred()) break; if (maybeTimedOut && !ShouldContinueFromTimeout()) return false; } if (!Connected()) { ReportConnectionError("SyncChannel"); return false; } // we just received a synchronous message from the other side. // If it's not the reply we were awaiting, there's a serious // error: either a mistimed/malformed message or a sync in-message // that raced with our sync out-message. // (NB: IPDL prevents the latter from occuring in actor code) // FIXME/cjones: real error handling NS_ABORT_IF_FALSE(mRecvd.is_sync() && mRecvd.is_reply() && (mRecvd.is_reply_error() || (mPendingReply == mRecvd.type() && msgSeqno == mRecvd.seqno())), "unexpected sync message"); mPendingReply = 0; *reply = mRecvd; mRecvd = Message(); return true; }
bool RPCChannel::OnMaybeDequeueOne() { // XXX performance tuning knob: could process all or k pending // messages here AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); Message recvd; { MonitorAutoLock lock(*mMonitor); if (!Connected()) { ReportConnectionError("RPCChannel"); return false; } if (!mDeferred.empty()) MaybeUndeferIncall(); MessageQueue *queue = mUrgent.empty() ? mNonUrgentDeferred.empty() ? &mPending : &mNonUrgentDeferred : &mUrgent; if (queue->empty()) return false; recvd = queue->front(); queue->pop_front(); } if (IsOnCxxStack() && recvd.is_rpc() && recvd.is_reply()) { // We probably just received a reply in a nested loop for an // RPC call sent before entering that loop. mOutOfTurnReplies[recvd.seqno()] = recvd; return false; } CxxStackFrame f(*this, IN_MESSAGE, &recvd); if (recvd.is_rpc()) Incall(recvd, 0); else if (recvd.is_sync()) SyncChannel::OnDispatchMessage(recvd); else AsyncChannel::OnDispatchMessage(recvd); return true; }
bool AsyncChannel::Echo(Message* _msg) { nsAutoPtr<Message> msg(_msg); AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route"); { MonitorAutoLock lock(*mMonitor); if (!Connected()) { ReportConnectionError("AsyncChannel"); return false; } mLink->EchoMessage(msg.forget()); } return true; }
bool RPCChannel::Call(Message* _msg, Message* reply) { RPC_ASSERT(!mPendingReply, "should not be waiting for a reply"); nsAutoPtr<Message> msg(_msg); AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); RPC_ASSERT(!ProcessingSyncMessage() || msg->priority() == IPC::Message::PRIORITY_HIGH, "violation of sync handler invariant"); RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here"); #ifdef OS_WIN SyncStackFrame frame(this, true); #endif Message copy = *msg; CxxStackFrame f(*this, OUT_MESSAGE, ©); MonitorAutoLock lock(*mMonitor); if (!Connected()) { ReportConnectionError("RPCChannel"); return false; } bool urgent = (copy.priority() == IPC::Message::PRIORITY_HIGH); msg->set_seqno(NextSeqno()); msg->set_rpc_remote_stack_depth_guess(mRemoteStackDepthGuess); msg->set_rpc_local_stack_depth(1 + StackDepth()); mStack.push(*msg); mLink->SendMessage(msg.forget()); while (1) { // if a handler invoked by *Dispatch*() spun a nested event // loop, and the connection was broken during that loop, we // might have already processed the OnError event. if so, // trying another loop iteration will be futile because // channel state will have been cleared if (!Connected()) { ReportConnectionError("RPCChannel"); return false; } // now might be the time to process a message deferred because // of race resolution MaybeUndeferIncall(); // here we're waiting for something to happen. see long // comment about the queue in RPCChannel.h while (!EventOccurred()) { bool maybeTimedOut = !RPCChannel::WaitForNotify(); if (EventOccurred() || // we might have received a "subtly deferred" message // in a nested loop that it's now time to process (!maybeTimedOut && (!mDeferred.empty() || !mOutOfTurnReplies.empty()))) break; if (maybeTimedOut && !ShouldContinueFromTimeout()) return false; } if (!Connected()) { ReportConnectionError("RPCChannel"); return false; } Message recvd; MessageMap::iterator it; if (!mOutOfTurnReplies.empty() && ((it = mOutOfTurnReplies.find(mStack.top().seqno())) != mOutOfTurnReplies.end())) { recvd = it->second; mOutOfTurnReplies.erase(it); } else if (!mUrgent.empty()) { recvd = mUrgent.front(); mUrgent.pop_front(); } else if (!mPending.empty()) { recvd = mPending.front(); mPending.pop_front(); } else { // because of subtleties with nested event loops, it's // possible that we got here and nothing happened. or, we // might have a deferred in-call that needs to be // processed. either way, we won't break the inner while // loop again until something new happens. continue; } if (!recvd.is_rpc()) { if (urgent && recvd.priority() != IPC::Message::PRIORITY_HIGH) { // If we're waiting for an urgent reply, don't process any // messages yet. mNonUrgentDeferred.push_back(recvd); } else if (recvd.is_sync()) { RPC_ASSERT(mPending.empty(), "other side should have been blocked"); MonitorAutoUnlock unlock(*mMonitor); CxxStackFrame f(*this, IN_MESSAGE, &recvd); SyncChannel::OnDispatchMessage(recvd); } else { MonitorAutoUnlock unlock(*mMonitor); CxxStackFrame f(*this, IN_MESSAGE, &recvd); AsyncChannel::OnDispatchMessage(recvd); } continue; } RPC_ASSERT(recvd.is_rpc(), "wtf???"); if (recvd.is_reply()) { RPC_ASSERT(0 < mStack.size(), "invalid RPC stack"); const Message& outcall = mStack.top(); // in the parent, seqno's increase from 0, and in the // child, they decrease from 0 if ((!mChild && recvd.seqno() < outcall.seqno()) || (mChild && recvd.seqno() > outcall.seqno())) { mOutOfTurnReplies[recvd.seqno()] = recvd; continue; } // FIXME/cjones: handle error RPC_ASSERT( recvd.is_reply_error() || (recvd.type() == (outcall.type()+1) && recvd.seqno() == outcall.seqno()), "somebody's misbehavin'", "rpc", true); // we received a reply to our most recent outstanding // call. pop this frame and return the reply mStack.pop(); bool isError = recvd.is_reply_error(); if (!isError) { *reply = recvd; } if (0 == StackDepth()) { RPC_ASSERT( mOutOfTurnReplies.empty(), "still have pending replies with no pending out-calls", "rpc", true); } // finished with this RPC stack frame return !isError; } // in-call. process in a new stack frame. // "snapshot" the current stack depth while we own the Monitor size_t stackDepth = StackDepth(); { MonitorAutoUnlock unlock(*mMonitor); // someone called in to us from the other side. handle the call CxxStackFrame f(*this, IN_MESSAGE, &recvd); Incall(recvd, stackDepth); // FIXME/cjones: error handling } } return true; }