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 + -
显示快捷键?