Beispiel #1
0
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::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, &copy);

    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;
}