void RPCChannel::MaybeUndeferIncall() { AssertWorkerThread(); mMonitor->AssertCurrentThreadOwns(); if (mDeferred.empty()) return; size_t stackDepth = StackDepth(); // the other side can only *under*-estimate our actual stack depth RPC_ASSERT(mDeferred.top().rpc_remote_stack_depth_guess() <= stackDepth, "fatal logic error"); if (mDeferred.top().rpc_remote_stack_depth_guess() < RemoteViewOfStackDepth(stackDepth)) return; // maybe time to process this message Message call = mDeferred.top(); mDeferred.pop(); // fix up fudge factor we added to account for race RPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error"); --mRemoteStackDepthGuess; mPending.push_back(call); }
void RPCChannel::OnChannelErrorFromLink() { AssertLinkThread(); mMonitor->AssertCurrentThreadOwns(); if (0 < StackDepth()) NotifyWorkerThread(); SyncChannel::OnChannelErrorFromLink(); }
bool RPCChannel::EventOccurred() const { AssertWorkerThread(); mMonitor->AssertCurrentThreadOwns(); RPC_ASSERT(StackDepth() > 0, "not in wait loop"); return (!Connected() || !mPending.empty() || !mUrgent.empty() || (!mOutOfTurnReplies.empty() && mOutOfTurnReplies.find(mStack.top().seqno()) != mOutOfTurnReplies.end())); }
static void DisplayStack(stackADT stack) { int i, depth; printf("Stos: "); depth=StackDepth(stack); if (depth==0) { printf("pusty\n"); } else { for (i=depth-1;i>=0; i--) { if (i<depth-1) printf(" "); printf("%g", GetStackElement(stack,i)); } printf("\n"); } }
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; }
void RPCChannel::OnMessageReceivedFromLink(const Message& msg) { AssertLinkThread(); mMonitor->AssertCurrentThreadOwns(); if (MaybeInterceptSpecialIOMessage(msg)) return; // regardless of the RPC stack, if we're awaiting a sync reply, we // know that it needs to be immediately handled to unblock us. if (AwaitingSyncReply() && msg.is_sync()) { // wake up worker thread waiting at SyncChannel::Send mRecvd = msg; NotifyWorkerThread(); return; } MessageQueue *queue = (msg.priority() == IPC::Message::PRIORITY_HIGH) ? &mUrgent : &mPending; bool compressMessage = (msg.compress() && !queue->empty() && queue->back().type() == msg.type() && queue->back().routing_id() == msg.routing_id()); if (compressMessage) { // This message type has compression enabled, and the back of // the queue was the same message type and routed to the same // destination. Replace it with the newer message. MOZ_ASSERT(queue->back().compress()); queue->pop_back(); } queue->push_back(msg); // There are three cases we're concerned about, relating to the state of // the main thread: // // (1) We are waiting on a sync reply - main thread is blocked on the IPC monitor. // - If the message is high priority, we wake up the main thread to // deliver the message. Otherwise, we leave it in the mPending queue, // posting a task to the main event loop, where it will be processed // once the synchronous reply has been received. // // (2) We are waiting on an RPC reply - main thread is blocked on the IPC monitor. // - Always wake up the main thread to deliver the message. // // (3) We are not waiting on a reply. // - We post a task to the main event loop. // bool waiting_rpc = (0 != StackDepth()); bool urgent = (msg.priority() == IPC::Message::PRIORITY_HIGH); if (waiting_rpc || (AwaitingSyncReply() && urgent)) { // Always wake up our RPC waiter, and wake up sync waiters for urgent // messages. NotifyWorkerThread(); } else { // Worker thread is either not blocked on a reply, or this is an // incoming RPC that raced with outgoing sync and needs to be deferred // to a later event-loop iteration. if (!compressMessage) { // If we compressed away the previous message, we'll reuse // its pending task. mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask)); } } }
bool PyrGC::ListSanity() { bool found; if (StackDepth() < 0) { fprintf(stderr, "stack underflow %d\n", (int)StackDepth()); return false; } //postfl("PyrGC::ListSanity\n"); for (int i=0; i<kNumGCSets; ++i) { PyrObjectHdr *obj; GCSet* set = mSets + i; // check black marker obj = &set->mBlack; if (!IsMarker(obj)) { //debugf("set %d black marker color wrong %d %p\n", i, obj->gc_color, obj); fprintf(stderr, "set %d black marker color wrong %d %p\n", i, obj->gc_color, obj); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); return false; } // check white marker obj = &set->mWhite; if (!IsMarker(obj)) { //debugf("set %d white marker color wrong %d %p\n", i, obj->gc_color, obj); fprintf(stderr, "set %d white marker color wrong %d %p\n", i, obj->gc_color, obj); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); return false; } // check free pointer between white and black marker if (set->mFree != &set->mBlack) { obj = set->mWhite.next; found = false; while (!IsMarker(obj)) { if (obj == set->mFree) { found = true; break; } obj = obj->next; } if (!found) { //debugf("set %d free pointer not between white and black\n", i); fprintf(stderr, "set %d free pointer not between white and black\n", i); fprintf(stderr, "set->mFree %p\n", set->mFree); fprintf(stderr, "set->mWhite %p\n", &set->mWhite); fprintf(stderr, "set->mBlack %p\n", &set->mBlack); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)set->mFree); fprintf(stderr, "black %d white %d grey %d\n", mBlackColor, mWhiteColor, mGreyColor); obj = &set->mWhite; int count = 0; do { if (obj == set->mFree) fprintf(stderr, "%4d %p %3d %d FREE\n", count, obj, obj->gc_color, obj->obj_sizeclass); else if (obj == &set->mWhite) fprintf(stderr, "%4d %p %3d %d WHITE\n", count, obj, obj->gc_color, obj->obj_sizeclass); else if (obj == &set->mBlack) fprintf(stderr, "%4d %p %3d %d BLACK\n", count, obj, obj->gc_color, obj->obj_sizeclass); else fprintf(stderr, "%4d %p %3d %d\n", count, obj, obj->gc_color, obj->obj_sizeclass); obj = obj->next; count++; } while (obj != &set->mWhite); return false; } } // scan black list obj = set->mBlack.next; while (!IsMarker(obj)) { if (obj->gc_color != mBlackColor) { //debugf("set %d black list obj color wrong %d (%d, %d, %d) %p\n", // i, obj->gc_color, mBlackColor, mGreyColor, mWhiteColor, obj); fprintf(stderr, "set %d black list obj color wrong %d (%d, %d, %d) %p\n", i, obj->gc_color, mBlackColor, mGreyColor, mWhiteColor, obj); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); return false; } if (GetGCSet(obj) != set) { //debugf("set %d black obj gcset wrong %d %p\n", i, obj->obj_sizeclass, obj); fprintf(stderr, "set %d black obj gcset wrong %d %p\n", i, obj->obj_sizeclass, obj); setPostFile(stderr); dumpBadObject((PyrObject*)obj); return false; } if (obj->next->prev != obj) { fprintf(stderr, "set %d black obj->next->prev != obj\n", i); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); } // scan for refs to white. if (!BlackToWhiteCheck((PyrObject*)obj)) return false; obj = obj->next; } // scan white list obj = set->mWhite.next; while (obj != set->mFree) { if (obj->gc_color != mWhiteColor) { //debugf("set %d white list obj color wrong %d (%d, %d, %d) %p\n", // i, obj->gc_color, mBlackColor, mGreyColor, mWhiteColor, obj); //debugf("hmmm free %p black %p\n", set->mFree, set->black); fprintf(stderr, "set %d white list obj color wrong %d (%d, %d, %d) %p\n", i, obj->gc_color, mBlackColor, mGreyColor, mWhiteColor, obj); fprintf(stderr, "hmmm free %p black %p\n", set->mFree, &set->mBlack); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); return false; } if (GetGCSet(obj) != set) { //debugf("set %d white obj gcset wrong %d %p\n", i, obj->obj_sizeclass, obj); fprintf(stderr, "set %d white obj gcset wrong %d %p\n", i, obj->obj_sizeclass, obj); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); return false; } if (obj->next->prev != obj) { fprintf(stderr, "set %d white obj->next->prev != obj\n", i); setPostFile(stderr); DumpBackTrace(mVMGlobals); dumpBadObject((PyrObject*)obj); } obj = obj->next; } // mark all free list items free obj = set->mFree; while (!IsMarker(obj)) { /*if (obj->gc_color == mGreyColor) { //debugf("grey obj on free list\n"); fprintf(stderr, "grey obj on free list\n"); return false; }*/ //post("FREE\n"); //dumpObject((PyrObject*)(PyrObject*)obj); obj->gc_color = mFreeColor; if (GetGCSet(obj) != set) { //debugf("set %d free obj gcset wrong %d %p\n", i, obj->obj_sizeclass, obj); fprintf(stderr, "set %d free obj gcset wrong %d %p\n", i, obj->obj_sizeclass, obj); //dumpObject((PyrObject*)obj); return false; } if (obj->next->prev != obj) { fprintf(stderr, "set %d free obj->next->prev != obj\n", i); //dumpObject((PyrObject*)obj); } obj = obj->next; } } int numgrey = 0; PyrObjectHdr *grey = mGrey.next; while (!IsMarker(grey)) { numgrey++; if (!IsGrey(grey)) { fprintf(stderr, "sc Object on grey list not grey %d %d %d\n", grey->gc_color, mGreyColor, numgrey); fprintf(stderr, "%p <- %p -> %p grey %p process %p\n", mGrey.prev, &mGrey, mGrey.next, grey, mProcess); return false; } grey = grey->next; } if (numgrey != mNumGrey) { fprintf(stderr, "grey count off %d %d\n", numgrey, mNumGrey); DumpInfo(); fprintf(stderr, "."); return false; } return true; }