kern_invoke.cxx
来自「C++ 编写的EROS RTOS」· CXX 代码 · 共 1,882 行 · 第 1/4 页
CXX
1,882 行
if (runState == RS_Available) /* Ensure that we awaken the sleeping threads (if any). */ wakeRoot = this; /******************************************************************** * AT THIS POINT we know that the invocation will complete in * principle. It is still possible that the invoker will block while * some part of the invokee gets paged in or while waiting for an * available Thread structure. The latter is a problem, and needs * to be dealt with. Note that the finiteness of the thread pool * isn't the source of the problem -- the real problem is that we * might not be able to fit all of the running domains in the swap * area. Eventually we shall need to implement a decongester to deal * with this, but that can wait. *********************************************************************/#ifdef GATEDEBUG dprintf(GATEDEBUG>3, "Checked for well-formed recipient\n");#endif if (inv.invokee && inv.invokee->IsWellFormed() == false) inv.invokee = 0; assert(inv.key->IsPrepared()); #ifdef GATEDEBUG dprintf(GATEDEBUG>2, "Invokee now valid\n");#endif assert(inv.invokee == 0 || inv.invokee->IsRunnable()); /* It does not matter if the invokee fails to prepare! * Note that we may be calling this with 'this == 0' */ inv.invokee->SetupExitBlock(inv);#ifdef OPTION_PURE_EXIT_STRINGS if (inv.validLen != 0) inv.invokee->SetupExitString(inv, inv.validLen);#endif#ifdef GATEDEBUG dprintf(GATEDEBUG>2, "Populated exit block\n");#endif #ifdef GATEDEBUG if (inv.suppressXfer) dprintf(true, "xfer is suppressed\n");#endif /* Identify the thread that will migrate to the recipient. Normally * it's the current thread. If this is a FORK invocation, it's a * new thread. Populate this thread consistent with the invokee. */ Thread *threadToMigrate = Thread::Current(); if (inv.invType == IT_Send) { Key& rk = *inv.entry.key[3]; /* If this is a send, and we are either (a) invoking a gate key, or (b) invoking a kernel key and passing a resume key to someone else, then we will need a new thread. That covers most cases. For real fun, consider a send on a domain key saying 'start this process' with resume key to third party in slot 3 -- we will (someday) handle that in the ProcessKey handler as a special case prior to the COMMIT_POINT() logic. */ if ( inv.key->IsGateKey() || rk.IsType(KT_Resume) ) { threadToMigrate = new Thread; threadToRelease = threadToMigrate; threadToMigrate->state = Thread::Ready;#ifdef GATEDEBUG dprintf(true, "Built new thread for fork\n");#endif } else threadToMigrate = 0; } #if defined(OPTION_DDB) && !defined(NDEBUG) bool invoked_gate_key = ( inv.key->IsGateKey() || (inv.key->IsType(KT_Returner) && inv.entry.key[3]->IsType(KT_Resume)) ); #if defined(DBG_WILD_PTR) || defined(TESTING_INVOCATION) if (dbg_wild_ptr) Check::Consistency("DoKeyInvocation() before invoking handler\n");#endif /* suppressXfer only gets set if this was a fault key, in which case * this is likely a re-invocation of the process by the keeper. */ if ( DDB_STOP(all) || (DDB_STOP(gate) && invoked_gate_key) || (DDB_STOP(keeper) && inv.suppressXfer) || (DDB_STOP(pflag) && ( (processFlags & PF_DDBINV) || (inv.invokee && inv.invokee->processFlags & PF_DDBINV) )) ) dprintf(true, "About to invoke key handler (inv.ty=%d) ic=%d\n", inv.keyType, KernStats.nInvoke);#endif#if defined(OPTION_KERN_TIMING_STATS) pre_handler = rdtsc();#endif keyHandler[inv.keyType](inv);#if defined(OPTION_KERN_TIMING_STATS) { extern uint32_t inv_delta_reset; if (inv_delta_reset == 0) { extern uint64_t inv_handler_cy; uint64_t post_handler = rdtsc(); inv_handler_cy += (post_handler - pre_handler); Invocation::KeyHandlerCycles[inv.keyType][inv.invType] += (post_handler - pre_handler); Invocation::KeyHandlerCounts[inv.keyType][inv.invType] ++; } } #endif#if defined(DBG_WILD_PTR) || defined(TESTING_INVOCATION) if (dbg_wild_ptr) Check::Consistency("DoKeyInvocation() after invoking handler\n");#endif assert (InvocationCommitted);#ifndef NDEBUG InvocationCommitted = false; #endif #if defined(OPTION_DDB) && !defined(NDEBUG) /* inv.suppressXfer only gets set if this was a fault key, in which * case this is likely a re-invocation of the process by the keeper. * FIX: This is no longer true */ if ( DDB_STOP(all) || ( DDB_STOP(gate) && invoked_gate_key ) || ( DDB_STOP(keeper) && inv.suppressXfer) || ( DDB_STOP(return) && inv.invType == IT_Reply ) || (DDB_STOP(pflag) && ( (processFlags & PF_DDBINV) || (inv.invokee && inv.invokee->processFlags & PF_DDBINV) )) || ( DDB_STOP(keyerr) && !invoked_gate_key && (inv.keyType != KT_Returner) && (inv.keyType != KT_Void) && (inv.exit.code != RC_OK) ) ) dprintf(true, "Before DeliverResult() (invokee=0x%08x)\n", inv.invokee); #endif /* Check the sanity of the receiving process in various ways: */ if (inv.invokee) { if (inv.invokee->IsNotRunnable()) { if (inv.invokee->procRoot == 0) { inv.invokee = 0; goto bad_invokee; } inv.invokee->Prepare(); if (inv.invokee->IsNotRunnable()) { inv.invokee = 0; goto bad_invokee; } } /* Invokee is okay. Deliver the result: */ inv.invokee->runState = RS_Running; if (!inv.suppressXfer) { inv.invokee->DeliverResult(inv);#if defined(DBG_WILD_PTR) || defined(TESTING_INVOCATION) if (dbg_wild_ptr) Check::Consistency("DoKeyInvocation() after DeliverResult()\n");#endif } /* If we are returning to ourselves, the resume key was never * generated. */ if (inv.invokee != this) inv.invokee->kr.ZapResumeKeys(); } bad_invokee: /* ONCE DELIVERRESULT IS CALLED, NONE OF THE INPUT CAPABILITIES REMAINS ALIVE!!! */ /* Clean up the invocation block: */ inv.Cleanup();#ifdef GATEDEBUG dprintf(GATEDEBUG>2, "Cleaned up invocation\n");#endif#ifdef GATEDEBUG dprintf(GATEDEBUG>2, "Updated invokee runstate\n");#endif if (threadToMigrate) { threadToMigrate->MigrateTo(inv.invokee);#ifdef OPTION_DDB if (inv.invokee->cpuReserve == &CpuReserve::InactiveReserve) dprintf(true, "Thread now in ctxt 0x%08x w/ bad schedule\n", inv.invokee);#endif if (inv.invType == IT_Send && inv.invokee) { threadToMigrate->Wakeup();#ifdef GATEDEBUG dprintf(true, "Woke up forkee\n");#endif } } if (wakeRoot) {#if 0 dprintf(false, "Wake up all of the losers sleeping on dr=0x%08x\n", wakeRoot);#endif wakeRoot->stallQ.WakeAll(); } #ifdef DBG_WILD_PTR { extern void ValidateAllThreads(); ValidateAllThreads(); }#endif#ifdef GATEDEBUG dprintf(GATEDEBUG>2, "Migrated the thread\n");#else#if 0 if ( invoked_gate_key ) dprintf(true, "Migrated the thread\n");#endif#endif inv.invokee = 0; threadToRelease = 0; #ifdef OPTION_KERN_TIMING_STATS { extern uint32_t inv_delta_reset; if (inv_delta_reset == 0) { extern uint64_t inv_delta_cy; uint64_t bot_time = rdtsc();#ifdef OPTION_KERN_EVENT_TRACING extern uint64_t inv_delta_cnt0; extern uint64_t inv_delta_cnt1; uint64_t bot_cnt0 = Machine::ReadCounter(0); uint64_t bot_cnt1 = Machine::ReadCounter(1); inv_delta_cnt0 += (bot_cnt0 - top_cnt0); inv_delta_cnt1 += (bot_cnt1 - top_cnt1);#endif inv_delta_cy += (bot_time - top_time); } else inv_delta_reset = 0; }#endif}#if 0voidProcess::DoKeyInvocation(){#ifdef INVOKE_TIMING uint64_t top_time = rdtsc();#if 0 uint64_t top_cnt0 = Machine::ReadCounter(0); uint64_t top_cnt1 = Machine::ReadCounter(1);#endif#endif KernStats.nInvoke++; inv.suppressXfer = false; /* suppress compiler bitching */ /* If preparation causes a depend entry to get zapped, it may be US * that gets zapped. Set up to check for that... */ PteZapped = false; ObjectHeader::BeginTransaction(); #ifndef NDEBUG assert (InvocationCommitted == false); InvocationCommitted = false; assert (inv.IsCorrupted() == false);#endif inv.nextPC = CalcPostInvocationPC(); /* in case we must restart */ AdjustInvocationPC();#ifdef GATEDEBUG dprintf(GATEDEBUG>2, "Reset gate PC\n");#endif threadToRelease = 0; #ifdef GATEDEBUG dprintf(GATEDEBUG>3, "Built recovery block\n");#endif#ifdef DBG_WILD_PTR if (dbg_wild_ptr) Check::Consistency("Begin DoKeyInvocation()");#endif /* Set up the entry block, faulting in any necessary data pages and * constructing an appropriate kernel mapping: */ SetupEntryBlock(inv);#ifdef GATEDEBUG dprintf(true, "Populated entry block\n");#endif assert(inv.key->IsPrepared()); /* There are two cases where the actual invocation may proceed on a * key other than the invoked key: * * Invocation of kept red segment key proceeds as invocation on * the keeper, AND observes the slot 2 convention of the format * key!!! Because this must overwrite slot 2, it must occur * following the entry block preparation. * * Gate key to malformed domain proceeds as invocation on void. * * The red seg test is done first because the extracted gate key (if * any) needs to pass the well-formed test too. */ if ( inv.key->IsType(KT_Segment) && inv.key->IsRedSegmentKey() && inv.key->IsReadOnly() == false && inv.key->IsNoCall() == false && inv.key->IsWeak() == false ) { /* dprintf(false, "It's a red segment key\n"); */ Node *redSegNode = (Node *) inv.key->GetObjectPtr(); Key& fmtKey = redSegNode->slot[RedSegFormat]; /* Only pass through if red segment is well-formed and has gate * key as keeper! */ if ( fmtKey.IsType(KT_Number) ) { uint32_t kprSlot = REDSEG_GET_KPR_SLOT(fmtKey.nk); if ( kprSlot != EROS_NODE_SLOT_MASK && redSegNode->slot[kprSlot].IsGateKey() ) { /* See if we need to clobber slot 2 with a node key to the red * segment. Need to do this BEFORE we bash inv.key... :-) */ if ( REDSEG_GET_SENDNODE(fmtKey.nk) == REDSEG_SEND_NODE ) { /* Not hazarded because invocation key */ inv.redScratchKey.NH_Set(*inv.key); inv.redScratchKey.SetType(KT_Node); inv.entry.key[2] = &inv.redScratchKey; inv.flags |= INV_REDSEGKEY; } /* Not hazarded because invocation key */ inv.key = &(redSegNode->slot[kprSlot]); inv.keyType = inv.key->GetType(); /* Prepared resume keys can only reside in dirty objects! */ if (inv.keyType == KT_Resume) redSegNode->MakeObjectDirty(); inv.key->Prepare(); /* MAY YIELD!!! */ inv.entry.w1 = fmtKey.nk.value[1]; inv.entry.w2 = fmtKey.nk.value[2]; } else dprintf(true, "Keeper not gate key or no keeper\n"); } else dprintf(true, "Format key is bad\n"); } assert(inv.key->IsPrepared()); inv.invokee = 0; /* until proven otherwise */ /* Right now a corner case here is buggered because we have not yet * updated the caller's runstate according to the call type. As a * result, a return on a start key to yourself won't work in this * code. */ if ( inv.key->IsGateKey() ) { assert (inv.key->IsPrepared()); /* Make a local copy (subvert alias analysis pessimism) */ Process *p = inv.key->gk.pContext; inv.invokee = p; p->Prepare(); /* may yield */#if 0 /* This is now checked in pk_GateKey.cxx */ if (inv.keyType == KT_Resume && inv.key->subType != KstResume) inv.suppressXfer = true;#endif if (p->IsWellFormed() == false) { /* Not hazarded because invocation key */ /* Pretend he invoked a void key. */ inv.key = &Key::VoidKey; inv.keyType = KT_Misc; inv.invokee = this; inv.entry.key[3] = &Key::VoidKey;#ifndef NDEBUG printf("Jumpee malformed\n");#endif }#ifndef NDEBUG else if (inv.keyType == KT_Resume && p->runState != RS_Waiting) { fatal("Resume key to wrong-state context\n"); }#endif else if (inv.keyType == KT_Start && p->runState != RS_Available) { Thread::Current()->SleepOn(p->stallQ); Thread::Current()->Yield(); }#if 0 else if ( inv.invType == IT_Call ) { BuildResumeKey(inv.resumeKey); inv.entry.key[3] = &inv.resumeKey; inv.flags |= INV_RESUMEKEY; }#endif } else if (inv.invType == IT_Call) { /* Call on non-gate key always returns to caller and requires no * resume key. */ inv.invokee = this; inv.entry.key[3] = &Key::VoidKey; } else { /* Kernel key invoked with REPLY or SEND. Key in slot 4 must be a * resume key, and if so the process must be waiting, else we will * not return to anyone. */ Key& rk = *inv.entry.key[3]; rk.Prepare(); /* Kernel keys return as though via the returner, so the key in * slot four must be a resume key to a party in the right state. */ if (rk.ktByte == KT_Resume) { Process *p = rk.gk.pContext; p->Prepare(); assert(p->runState == RS_Waiting); inv.invokee = p;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?