Exemple #1
0
void
SchedulerTimer::init(MemoryMgrPrimitive * memory)
{
    SchedulerTimer tmp;
    // Get vtbl pointer right
    memcpy(this, &tmp, sizeof(tmp));
    anchor = 0;
    now = 0;
    when=SysTime(-1);
    uval ptr;
    memory->alloc(ptr, TABLE_SIZE*sizeof(AutoListHead), PAGE_SIZE);
    table = (AutoListHead*)ptr;

    uval i = 0;

    // We need to legitimately construct this object, and then make
    // copies of it.  This ensures that vtable pointers are set right
    // (we don't "construct" array elements).
    AutoListHead alh;
    alh.init();
    memcpy(&immediate, &alh, sizeof(AutoListHead));
    immediate.init();
    for (; i<TABLE_SIZE; ++i) {
	memcpy(&table[i],&alh, sizeof(AutoListHead));
	table[i].init();
    }
}
Exemple #2
0
 /** Method that is called after execution of the task. */
 void End()
 {
   DBG_ASSERT(!end_called_);
   DBG_ASSERT(start_called_);
   end_called_ = true;
   start_called_ = false;
   total_cputime_ += CpuTime() - start_cputime_;
   total_systime_ += SysTime() - start_systime_;
   total_walltime_ += WallclockTime() - start_walltime_;
 }
Exemple #3
0
 /** Method that is called before execution of the task. */
 void Start()
 {
   DBG_ASSERT(end_called_);
   DBG_ASSERT(!start_called_);
   end_called_ = false;
   start_called_ = true;
   start_cputime_ = CpuTime();
   start_systime_ = SysTime();
   start_walltime_ = WallclockTime();
 }
Exemple #4
0
 /** Method that is called after execution of the task for which
  *  timing might have been started.  This only updates the timing
  *  if the timing has indeed been conducted. This is useful to
  *  stop timing after catching exceptions. */
 void EndIfStarted()
 {
   if (start_called_) {
     end_called_ = true;
     start_called_ = false;
     total_cputime_ += CpuTime() - start_cputime_;
     total_systime_ += SysTime() - start_systime_;
     total_walltime_ += WallclockTime() - start_walltime_;
   }
   DBG_ASSERT(end_called_);
 }
Exemple #5
0
/*
 * if timer ever uses storage to do a more efficient job of managing
 * the set of timer events, then the PostFork init will differ from
 * the init above
 */
/* virtual */ void
SchedulerTimer::initPostFork(ForkData *fd)
{
    AutoListHead alh;
    alh.init();
    memcpy(&immediate, &alh, sizeof(AutoListHead));
    immediate.init();
    when = SysTime(-1);
    anchor = NULL;
    table = fd->table;
    for (uval i = 0; i < TABLE_SIZE; i++) {
	table[i].init();
    }
}
Exemple #6
0
void
Clock::MeasureFps()
{
	cnt++;

	if(cnt>timing)
	{
		double current = SysTime();
		double elapsed = current - last;
		
		// reset
		cnt=0;
		last = current;

		fps = timing/elapsed;
	}
}
Exemple #7
0
void CgFXPassChunk::updateStateUniforms(DrawEnv  *pEnv)
{
    CgFXMaterial *pMat = _sfMaterial.getValue();

    OSG_ASSERT(pMat != NULL);

          CGeffect     pEffect        = pMat->getEffect        ();
          UInt32       uiStateVars    = pMat->getStateVariables();
    const std::string *vStateVarNames = pMat->getStateVarNames ();

          UInt32       uiMask         = 0x0001;


    OSG_ASSERT(pEffect != NULL);

    Matrix mObj2World = pEnv->getObjectToWorld();

    std::string szTmp;

    for(UInt32 i = 0; i < CgFXMaterial::NumStateVars; ++i)
    {
        if(uiStateVars == 0x0000)
            break;
        
        switch(uiStateVars & uiMask)
        {
            case CgFXMaterial::CgProjectionMask:
            {
                CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgProjection].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                cgGLSetMatrixParameterfc(
                    pMatrixParam,
                    pEnv->getCameraFullProjection().getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;

            case CgFXMaterial::CgModelViewProjectionMask:
            {
                CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgModelViewProjection].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                Matrix mWorld2Scrn = pEnv->getWorldToScreen();

                mWorld2Scrn.mult(mObj2World);                

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mWorld2Scrn.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;


            // -------------
            // Model | World
            // -------------
            case CgFXMaterial::CgModelMask:
            {
                CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgModel].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mObj2World.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;
            case CgFXMaterial::CgModelIMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgModelI].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                Matrix mModelI = mObj2World;

                mModelI.invert();

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mModelI.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
           }
            break;
            case CgFXMaterial::CgModelITMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgModelIT].c_str());

                OSG_ASSERT(pMatrixParam != NULL);

                Matrix mModelIT = mObj2World;

                mModelIT.invert   ();
                mModelIT.transpose();

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mModelIT.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;


            // ---------------------
            // ModelView | WorldView
            // ---------------------
            case CgFXMaterial::CgModelViewMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[CgFXMaterial::CgModelView].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                Matrix mCameraViewing = pEnv->getCameraViewing();

                mCameraViewing.mult(mObj2World);
                
                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mCameraViewing.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;
            case CgFXMaterial::CgModelViewIMask:
            {
                CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[CgFXMaterial::CgModelViewI].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                Matrix mCameraViewing = pEnv->getCameraViewing();

                mCameraViewing.mult(mObj2World);
                mCameraViewing.invert();

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mCameraViewing.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;
            case CgFXMaterial::CgModelViewITMask:
            {
                CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[CgFXMaterial::CgModelViewIT].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                Matrix mCameraViewing = pEnv->getCameraViewing();

                mCameraViewing.mult     (mObj2World);
                mCameraViewing.invert   (          );
                mCameraViewing.transpose(          );

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mCameraViewing.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;


            // -------------
            // View
            // -------------
            case CgFXMaterial::CgViewMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgView].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         pEnv->getCameraViewing().getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;
            case CgFXMaterial::CgViewIMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgViewI].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                OSG::Matrix mCameraViewing = pEnv->getCameraViewing();

                mCameraViewing.invert();

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mCameraViewing.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;
            case CgFXMaterial::CgViewITMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgViewIT].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                OSG::Matrix mCameraViewing = pEnv->getCameraViewing();

                mCameraViewing.invert   ();
                mCameraViewing.transpose();

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         mCameraViewing.getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;


            case CgFXMaterial::CgViewProjectionMask:
            {
                 CGparameter pMatrixParam = 
                    cgGetNamedEffectParameter(
                        pEffect, 
                        vStateVarNames[
                            CgFXMaterial::CgViewProjection].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);

                OSG_ASSERT(pMatrixParam != NULL);

                cgGLSetMatrixParameterfc(pMatrixParam,
                                         pEnv->getWorldToScreen().getValues());
                CgFXMaterial::checkForCgError("cgGLSetMatrixParameterfc", NULL);
            }
            break;

            case CgFXMaterial::CgTimeMask:
            {
                CGparameter pTime = cgGetNamedEffectParameter(
                                                              pEffect,
                                                              vStateVarNames[
                                                              CgFXMaterial::CgTime].c_str());
                CgFXMaterial::checkForCgError("cgGetNamedEffectParameter", NULL);
                OSG_ASSERT(pTime != NULL);

                static const UInt16 MaxLeftDecDigits(4);

                //getSystemTime() returns a time value as a 64-bit floating
                //point number.  But the time value taken by Cg is a 32-bit
                //float.  This can cause a problem with precision when
                //getSystemTime() returns large values, that truncate when cast
                //to a 32-bit float.
                //
                //To deal with this, we are removing the most significant
                //decimal digits left of the decimal points after the MaxLeftDecDigits
                //one
                Time SysTime(OSG::getSystemTime());

                Time Base10Shift(osgPow<Time>(10,MaxLeftDecDigits));
                Time TruncValue(SysTime - (floor(SysTime / Base10Shift) * Base10Shift));
                
                cgSetParameter1f(pTime, static_cast<Real32>(TruncValue));
            }
            break;

            default:
                break;
        };

        uiStateVars &= ~uiMask;
        uiMask      =   uiMask << 1;
    }
}
int getSysTime(float modelBaseRate){
if (m_systime == 0) {
        m_systime = SysTime (modelBaseRate) ; //after timer has been init, set to 1
    }
    return m_systime ;
}
Exemple #9
0
void
SchedulerTimer::timerInterrupt(SoftIntr::IntrType)
{
    // use when as estimate of time unless its cheap to read
    // the clock
    // FIXME:  This usermodeClock trick assumes that interrupts won't be
    //         delivered earlier than expected, an assumption that is
    //         violated when a dispatcher is migrated from one physical
    //         processor to another.  An early interrupt will cause us to
    //         invoke event handlers before their expected times.  To fix
    //         this problem, we should pass the current time up to the
    //         dispatcher when we generate a soft timer interrupt for it.
    SysTime old = when;
    SysTime start = old;
    AutoListHead runList;

    runList.init();
    now = usermodeClock?getClock():when;

    TimerEvent* cur;
    if (unlikely(now < when)) {
	if (when == SysTime(-1)) {
	    now = _TIMER_REQUEST(when, TimerEvent::reset);
	} else {
	    now = _TIMER_REQUEST(when,TimerEvent::absolute);
	}
    }
    while (now > when) {

	cur = anchor;
	// Run the current timer
	anchor = NULL;
	if (cur) {
	    cur->lockedDetach();
//	    tassertWrn(old <= cur->when, "stale event: %lx %lx %lx\n",
//		       uval(old), uval(cur->when), uval(now));
	    runList.prepend(cur);
	}

	cur = (TimerEvent*)immediate.next();
	// Scan immediate timer list, looking for expired timers,
	// first timer to go off next.
	while (cur) {
	    TimerEvent *te = cur;
	    cur = (TimerEvent*)cur->next();
	    tassertMsg(bucketEnd(now)>=te->when,
		       "Bad event on immediate list: %lx %lx %p\n",
		       uval(now), uval(te->when), te);

	    if (te->when <= now) {
		te->lockedDetach();
		tassertWrn(old <= te->when, "stale event3: %lx %lx %lx\n",
			   uval(old), uval(te->when), uval(now));
		runList.prepend(te);
	    } else if (!anchor ||
		       te->when <= anchor->when) {
		anchor = te;
		when = te->when;
	    }
	}

	// Move all events for buckets containing  "start" to "now"
	// to immediate list
	migrate(start, now, runList);


	if (!anchor) {
	    // No "anchor" timer to go off next, look for the next bucket
	    // with timers.

	    // we start in current slot, then increment
	    SysTime nextSlot = bucketStart(now + BUCKET_TICKS);
	    while (timeIndex(old) != timeIndex(nextSlot)
		   && !table[timeIndex(nextSlot)].next()) {
		nextSlot += BUCKET_TICKS;
	    }

	    if (timeIndex(now) == timeIndex(nextSlot)
		&& !table[timeIndex(nextSlot)].next()) {

		when = SysTime(-1);
	    } else {
		when = nextSlot;
	    }
	}

#if 0

	if (when==SysTime(-1)) {
	    SchedulerTimer *st = &DISPATCHER->timer;
	    for (uval i = 0; i<TABLE_SIZE; ++i) {
		TimerEvent *t = (TimerEvent*)st->table[i].next();
		tassertMsg(t==NULL,
			   "Existing event, no timer: %lx %p %p %p %p\n",
			   i,t,st,DISPATCHER,this);
	    }
	}
	{
	    SchedulerTimer *st = &DISPATCHER->timer;
	    for (uval i = 0; i<TABLE_SIZE; ++i) {
		TimerEvent *t = (TimerEvent*)st->table[i].next();
		while (t) {
		    tassertMsg(timeIndex(t->when) != timeIndex(now) ||
			       t->when > bucketEnd(now),
			       "event not in immediate: %p %lx %lx %lx\n",t,
			       uval(now), uval(t->when), uval(when));

		    tassertMsg(t->when > now, "stale event in table %p\n", t);

		    t = (TimerEvent*)t->next();

		}
	    }
	}
#endif
	// adjust timer only if it has changed
	if (when != old) {
	    if (when == SysTime(-1)) {
		now = _TIMER_REQUEST(when, TimerEvent::reset);
	    } else {
		now = _TIMER_REQUEST(when,TimerEvent::absolute);
	    }
	}
    }

    // Run expired timers We do this after all of the processing abov,
    // rather than as we encounter these events because handleEvent()
    // may lead to SchedulerTimer::disabledScheduleEvent, and we don't
    // want that code to run while the timer object's state is in the
    // midst of the computations above -- we want that code to run on
    // a clean slate. Only now can we run these callbacks while
    // ensuring that SchedulerTimer is fully cleaned up.
    cur = (TimerEvent*)runList.next();
    while (cur) {
	TimerEvent *te = (TimerEvent*)cur;
	cur = (TimerEvent*)cur->next();
	te->lockedDetach();
	te->handleEvent();
    }
//    err_printf("next timer: %p %lx\n", DISPATCHER, uval(when));
}
Exemple #10
0
/*
 *
 * runs disabled to protect timer event structures.
 * also, _TIMER_REQUEST must only be called disabled
 */
SysTime
SchedulerTimer::disabledScheduleEvent(TimerEvent* timerEvent,
				      SysTime whenTime, TimerEvent::Kind kind)
{
#if 0
    // kludge which can be used to catch unpinned timer events in the
    // kernel
    passertMsg((uval)timerEvent >= 0xc000000000000000ul ||
	       (uval)timerEvent < 0x8000000000000000ul, "opps\n");
#endif
    AutoListNode *list = NULL;
    SysTime old = when;
    switch (kind) {
    default:
	return 0;

    case TimerEvent::queryTicksPerSecond:
	return kernelInfoLocal.systemGlobal.ticksPerSecond;

    case TimerEvent::queryNow:
	if (usermodeClock) {
	    now = getClock();
	} else {
	    now = _TIMER_REQUEST(0, TimerEvent::queryNow);
	}
	break;

    case TimerEvent::reset:

	list = timerEvent->head();
	if (!list) {
	    break;
	}

	timerEvent->lockedDetach();
	if (likely(timerEvent != anchor &&
		   // timerEvent bucket is non-empty
		   !(when == bucketStart(timerEvent->when)
		     && !list->next()))) {
	    break;
	}

	if (timerEvent == anchor) {
	    anchor = NULL;
	    list = immediate.next();
	    when = SysTime(-1);
	    while (list) {
		TimerEvent* te = (TimerEvent*)list;
		list = list->next();
		if (!anchor || anchor->when > te->when) {
		    anchor = te;
		    when = anchor->when;
		}
	    }
	}
	if (!anchor) {
	    SysTime currSlot = bucketStart(timerEvent->when + BUCKET_TICKS);
	    while (timeIndex(currSlot)!= timeIndex(timerEvent->when)
		   && !table[timeIndex(currSlot)].next()) {
		currSlot += BUCKET_TICKS;
	    }

	    if (timeIndex(currSlot) == timeIndex(timerEvent->when)) {
		when = SysTime(-1);
	    } else {
		when = currSlot;
	    }
	}
	if ( old != when ) {
	    if (when == SysTime(-1)) {
		now = _TIMER_REQUEST(when, TimerEvent::reset);
	    } else {
		now = _TIMER_REQUEST(when, TimerEvent::absolute);
	    }
	}
	tassertMsg(when == SysTime(-1) ||
		   (!anchor && (when == bucketStart(when))) ||
		   (anchor && when == anchor->when), "bad timer setting\n");

	break;

    case TimerEvent::relative:
	tassertMsg(timerEvent->head() == NULL, "event on list\n");
	if (unlikely(whenTime==0)) {
	    now = _TIMER_REQUEST(0, TimerEvent::queryNow);
	    timerEvent->when = now;
	    timerEvent->lockedDetach();
	    timerEvent->handleEvent();
	    break;
	}
	if (!usermodeClock) {
	    // N.B. this call changes the event time only if the
	    // new event is sooner than the current event if any
	    now = _TIMER_REQUEST(whenTime, TimerEvent::relative);
	    whenTime += now;
	} else {
	    // we can read the clock cheaply
	    now = getClock();
	    whenTime+=now;
	}

	// fall through to absolute case


    case TimerEvent::absolute:
#if 0
	//code to catch someone setting small timeouts
	if (
	    whenTime-now<marcLimit) {
	    // don't complain about what is most likely the
	    // dispatcher two minute warning
//	    if (!(DREFGOBJ(TheProcessRef)->getPID() == 0 &&
//		  whenTime-now == 20000)) {
		err_printf("small whenTime %lld pid %ld\n",
			   whenTime-now, DREFGOBJ(TheProcessRef)->getPID());
	}
#endif /* #if 0 */

	tassertMsg(timerEvent->head() == NULL, "event on list\n");

	if (unlikely(whenTime <= now)) {
	    now = _TIMER_REQUEST(0, TimerEvent::queryNow);
	    timerEvent->when = now;
	    timerEvent->lockedDetach();
	    timerEvent->handleEvent();
	    break;
	}

	timerEvent->when = whenTime;


	if (timerEvent->when <= bucketEnd(now)) {
	    // Event is in immediate window
	    if (!anchor || anchor->when > timerEvent->when) {
		// Event is next to go off --- requires timer adjustment
		tassertMsg(!anchor ||
			   timerEvent->when <= bucketEnd(anchor->when),
			   "event not in immediate bucket\n");
		immediate.append(timerEvent);
		anchor = timerEvent;
		when = whenTime;

		// We may have already set the timer above
		if (usermodeClock) {
		    if (old != when) {
			now = _TIMER_REQUEST(when, TimerEvent::absolute);
		    }
		}
		tassertMsg(when == SysTime(-1) ||
			   (!anchor && (when == bucketStart(when))) ||
			   (anchor && when == anchor->when),
			   "bad timer setting\n");
		break;
	    }
	    tassertMsg(!anchor ||
		       timerEvent->when <= bucketEnd(now),
		       "event not in immediate bucket\n");
	    immediate.prepend(timerEvent);
	} else {
	    // Dump event into the right bucket
	    table[timeIndex(timerEvent->when)].append(timerEvent);
	    if (timerEvent->when < when) {

		// Set timer for bucket of this event
		when = bucketStart(timerEvent->when);
		anchor = NULL;
		if (old != when) {
		    now = _TIMER_REQUEST(when, TimerEvent::absolute);
		}
		tassertMsg(when == SysTime(-1) ||
			   (!anchor && (when == bucketStart(when))) ||
			   (anchor && when == anchor->when),
			   "bad timer setting\n");

	    }
	}
	tassertMsg(when <= timerEvent->when,
		   "No timer before scheduled event: %p %p\n",
		   this, timerEvent);
	tassertMsg(when == SysTime(-1) ||
		   (!anchor && (when == bucketStart(when))) ||
		   (anchor && when == anchor->when), "bad timer setting\n");
    }

#if 0
    {
	SchedulerTimer *st = this;
	for (uval i = 0; i<TABLE_SIZE; ++i) {
	    TimerEvent *t = (TimerEvent*)st->table[i].next();
	    while (t) {
		tassertMsg(timeIndex(t->when) != timeIndex(now) ||
			   t->when > bucketEnd(now),
			   "event not in immediate: %p %lx %lx %lx %lx\n",
			   timerEvent,
			   uval(now), uval(now), uval(t->when), uval(when));
		tassertMsg(t->when > now, "stale event in table %p\n", t);

		t = (TimerEvent*)t->next();

	    }
	}
    }
#endif
    return now;
}
Exemple #11
0
SysStatus
ProcessVPList::detachDispatcher(CPUDomainAnnex *cda, DispatcherID dspid,
				HATRef hatRef)
{
    SysStatus rc;
    VPInfo *vpInfo;
    ProcessAnnex *pa;
    uval64 ipcRetryIDs;

    tassertMsg(cda->getPP() == Scheduler::GetVP(), "CDA not on this pp.\n");

    RDNum rd; VPNum vp;
    SysTypes::UNPACK_DSPID(dspid, rd, vp);

    if (requests.enter() < 0) {
	return _SERROR(2642, 0, ESRCH);	// process being destroyed
    }

    rc = findProcessAnnex(rd, vp, vpInfo, pa);
    if (_FAILURE(rc)) {
	requests.leave();
	return rc;
    }

    if (!pa->isAttached(cda)) {
	requests.leave();
	return _SERROR(2643, 0, EINVAL);
    }

    vpInfo->lock.acquire();

    disableHardwareInterrupts();

    if (pa->reservedThread != NULL) {
	/*
	 * FIXME:  For now, don't try to detach a dispatcher that is currently
	 *         disabled.  We have to do better in the long run.
	 */
	enableHardwareInterrupts();
	rc = _SERROR(2312, 0, EAGAIN);
	goto CleanupAndReturn;
    }

    pa->detach();
    exceptionLocal.ipcTargetTable.remove(pa);

    if (KernelTimer::TimerRequestTime(pa) != SysTime(-1)) {
	/*
	 * PA has a timeout request registered.  Rather than try to reproduce
	 * it on the new processor, we simply generate a TIMER_EVENT soft
	 * interrupt so that the dispatcher can sort things out for itself.
	 */
	(void) pa->dispatcher->interrupts.fetchAndSet(SoftIntr::TIMER_EVENT);
    }
    exceptionLocal.kernelTimer.remove(pa);

    ipcRetryIDs = IPCRetryManager::GetIPCRetryIDs(pa);
    if (ipcRetryIDs != 0) {
	/*
	 * PA has IPCs waiting to be retried.  Simply generate notifications
	 * for all of them, to be delivered when the dispatcher runs.
	 */
	pa->dispatcher->ipcRetry |= ipcRetryIDs;
	(void) pa->dispatcher->interrupts.
				fetchAndSet(SoftIntr::IPC_RETRY_NOTIFY);
    }
    exceptionLocal.ipcRetryManager.remove(pa);

    enableHardwareInterrupts();

    vpInfo->dspCounter--;
    if (vpInfo->dspCounter > 0) {
	rc = 0;
	goto CleanupAndReturn;
    }

    /*
     * This VP's last dispatcher has now been detached, so detach the VP.
     * Switch to the canonical kernel address space, in case we're currently
     * "borrowing" the address space we're about to unmap.
     */
    ((HATKernel*)(DREFGOBJK(TheKernelHATRef)))->switchToKernelAddressSpace();

    rc = DREF(hatRef)->detachVP(vp);
    tassertMsg(_SUCCESS(rc), "hat->detachVP() failed.\n");

    vpInfo->pp = ProcessAnnex::NO_PHYS_PROC; // VP now ready for re-attachment
    rc = 0;

CleanupAndReturn:
    vpInfo->lock.release();
    requests.leave();
    return rc;
}