diff -r 000000000000 -r 96e5fb8b040d kernel/eka/nkernsmp/sched.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/nkernsmp/sched.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,823 @@ +// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32\nkernsmp\sched.cpp +// +// + +// NThreadBase member data +#define __INCLUDE_NTHREADBASE_DEFINES__ + +// TDfc member data +#define __INCLUDE_TDFC_DEFINES__ + +#include "nk_priv.h" +#include + +TSpinLock NEventHandler::TiedLock(TSpinLock::EOrderEventHandlerTied); + +/****************************************************************************** + * TScheduler + ******************************************************************************/ + +// TScheduler resides in .bss so other fields are zero-initialised +TScheduler::TScheduler() + : iActiveCpus1(1), // only boot CPU for now + iActiveCpus2(1), // only boot CPU for now + iIdleSpinLock(TSpinLock::EOrderIdleDFCList), + iCpusNotIdle(1) // only boot CPU for now + { + TInt i; + for (i=0; iiScheduler = this; + s->iCpuNum = TUint32(i); + s->iCpuMask = 1u<AcqSLock(); + } + +void NSchedulable::RelSLock() + { + if (iParent!=this && iParent) + iParent->RelSLock(); + iSSpinLock.UnlockOnly(); + } + +void NSchedulable::LAcqSLock() + { + NKern::Lock(); + AcqSLock(); + } + +void NSchedulable::RelSLockU() + { + RelSLock(); + NKern::Unlock(); + } + +void NSchedulable::UnPauseT() + { + CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NSchedulable::UnPauseT"); + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T nUnPauseT",this)); + __NK_ASSERT_DEBUG(iPauseCount); + if (--iPauseCount || iReady || iSuspended || (iParent && ((NThread*)this)->iWaitState.ThreadIsBlocked())) + return; + ReadyT(EUnPause); + } + +void NSchedulable::DeferredReadyIDfcFn(TAny* aPtr) + { + NSchedulable* a = (NSchedulable*)aPtr; + a->AcqSLock(); + TUint32 evs = __e32_atomic_and_acq32(&a->iEventState, ~EDeferredReady); + if (evs & EDeferredReady) + { + if (a->iParent) + { + // thread + a->UnPauseT(); + } + else + { + // thread group + NThreadGroup* g = (NThreadGroup*)a; + __KTRACE_OPT(KNKERN,DEBUGPRINT("%G nDeferredReady",g)); + __NK_ASSERT_DEBUG(g->iPauseCount); + if (--g->iPauseCount && g->iNThreadList.NonEmpty()) + g->ReadyT(EUnPause); + } + } + a->RelSLock(); + } + +TInt NSchedulable::AddTiedEvent(NEventHandler* aEvent) + { + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T AddEv %08x",this,aEvent)); + TInt r = KErrGeneral; + NEventHandler::TiedLock.LockOnly(); + AcqSLock(); + if (iStopping) + r = KErrDied; + else if (!aEvent->iTied) + { + aEvent->iTied = this; + iEvents.Add(&aEvent->iTiedLink); + r = KErrNone; + } + RelSLock(); + NEventHandler::TiedLock.UnlockOnly(); + return r; + } + +void ipi_dummy(TGenericIPI*) + { + } + +/** Detach and cancel any tied events attached to this thread/group + +Call in a thread context with interrupts and preemption enabled. +Calling thread in critical section. + +@internalComponent +*/ +void NSchedulable::DetachTiedEvents() + { + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T DetTiedEv",this)); + NKern::Lock(); + NEventHandler::TiedLock.LockOnly(); + AcqSLock(); + iStopping = TRUE; + if (!iParent) + { + // can't destroy a group until all threads have detached from it + NThreadGroup* g = (NThreadGroup*)this; + __NK_ASSERT_ALWAYS(g->iThreadCount==0 && g->iNThreadList.IsEmpty()); + } + RelSLock(); + NEventHandler::TiedLock.UnlockOnly(); + + // send IPI to all processors to synchronise + // after this, any tied IDFCs can only proceed to completion + // they can't be queued again + TGenericIPI ipi; + ipi.QueueAllOther(&ipi_dummy); + NKern::Unlock(); + ipi.WaitCompletion(); + + FOREVER + { + NKern::Lock(); + NEventHandler::TiedLock.LockOnly(); + AcqSLock(); + NEventHandler* h = 0; + TInt type = -1; + if (!iEvents.IsEmpty()) + { + h = _LOFF(iEvents.First()->Deque(), NEventHandler, iTiedLink); + h->iTiedLink.iNext = 0; + type = h->iHType; + } + RelSLock(); + if (type == NEventHandler::EEventHandlerNTimer) + { + // everything's easy for a timer since we can just cancel it here + NTimer* tmr = (NTimer*)h; + tmr->DoCancel(NTimer::ECancelDestroy); + tmr->iTied = 0; + } + else if (type == NEventHandler::EEventHandlerIDFC) + { + // can just cancel the IDFC with TiedLock held + // EndTiedEvent() may be delayed, but we wait for that further down + // iTied will have been captured before the IDFC state is reset + // Cancel() waits for the state to be reset + TDfc* d = (TDfc*)h; + d->Cancel(); + d->iHType = (TUint8)NEventHandler::EEventHandlerDummy; + d->iTied = 0; + } + NEventHandler::TiedLock.UnlockOnly(); + NKern::Unlock(); + if (!h) + break; + switch (type) + { + case NEventHandler::EEventHandlerIrq: + { + NIrqHandler* pH = (NIrqHandler*)h; + // pH can't have been freed since we dequeued it but left iTied set + pH->Unbind(pH->iHandle, this); + break; + } + case NEventHandler::EEventHandlerNTimer: + case NEventHandler::EEventHandlerIDFC: + case NEventHandler::EEventHandlerDummy: + // nothing left to do + break; + default: + __NK_ASSERT_ALWAYS(0); + break; + } + } + + // Wait for any remaining tied event handlers to complete + while (iEventState & EEventCountMask) + { + __chill(); + } + } + +/****************************************************************************** + * NThreadGroup + ******************************************************************************/ + + +/****************************************************************************** + * NThreadBase + ******************************************************************************/ + +/** Makes a nanothread ready. + + For use by RTOS personality layers. + + @pre Kernel must be locked. + @pre Call either in a thread or an IDFC context. + @pre The thread being made ready must not be explicitly suspended + + @post Kernel is locked. + */ +void NSchedulable::ReadyT(TUint aMode) + { + CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NSchedulable::ReadyT"); + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T nReadyT(%x)",this,aMode)); + NThreadBase* t = (NThreadBase*)this; +#ifdef _DEBUG + if (!iParent) + t = (NThreadBase*)0xface0fff; +#endif + __NK_ASSERT_DEBUG(!iReady && (!iParent || (!t->iWaitState.iWtC.iWtStFlags && !t->iPauseCount && !t->iSuspended))); + TSubScheduler& ss0 = SubScheduler(); + NSchedulable* g = this; + if (iParent != this && iParent) + { + NThreadGroup* tg = (NThreadGroup*)iParent; + iReady = EReadyGroup; + if (tg->iReady) + { + // extra thread added to group - change priority if necessary + tg->iNThreadList.Add(this); + TInt gp = tg->iPriority; + TSubScheduler& ss = TheSubSchedulers[tg->iReady & EReadyCpuMask]; + ss.iReadyListLock.LockOnly(); + TInt hp = ss.HighestPriority(); + if (iPriority>gp) + ss.ChangePriority(tg, iPriority); + if (iPriority>hp || (iPriority==hp && ss.iCurrentThread && ss.iCurrentThread->iTime==0)) + { + if (&ss == &ss0) + RescheduleNeeded(); // reschedule on this processor + else + ss0.iReschedIPIs |= ss.iCpuMask; // will kick the other CPU when this CPU reenables preemption + } + if ((aMode & ENewTimeslice) && t->iTime==0 && (iNext!=this || ss.iQueue[iPriority])) + t->iTime = t->iTimeslice; + ss.iReadyListLock.UnlockOnly(); + return; + } + tg->iNThreadList.Add(this); + tg->iPriority = iPriority; // first in group + g = tg; // fall through to add group to subscheduler + } + TInt cpu = -1; + if (aMode & EUnPause) + { + cpu = (g->iEventState & EThreadCpuMask)>>EThreadCpuShift; + if (CheckCpuAgainstAffinity(cpu, g->iCpuAffinity)) + goto cpu_ok; + } + else if (g->iFreezeCpu) + { + cpu = g->iLastCpu; + if (!CheckCpuAgainstAffinity(cpu, g->iCpuAffinity)) + g->iCpuChange = TRUE; + } + else if (!(g->iCpuAffinity & NTHREADBASE_CPU_AFFINITY_MASK)) + cpu = g->iCpuAffinity; + else if ((aMode & EPreferSameCpu) && (g->iCpuAffinity & ss0.iCpuMask)) + cpu = ss0.iCpuNum; + if (cpu < 0) + { + // pick a cpu + TScheduler& s = TheScheduler; + TUint32 m = g->iCpuAffinity & s.iActiveCpus1; + TInt i; + TInt lowest_p = KMaxTInt; + for (i=0; i lowest_p) + continue; + if (cpu>=0 && g->iLastCpu!=i) + continue; + lowest_p = hp; + cpu = i; + } + } +cpu_ok: + __NK_ASSERT_ALWAYS(cpu>=0); + if (g->TiedEventReadyInterlock(cpu)) + { + __KTRACE_OPT(KSCHED2,DEBUGPRINT("ReadyT->CPU %dD",cpu)); + ++g->iPauseCount; +// ((TDfc*)g->i_IDfcMem)->Add(); + return; + } + __KTRACE_OPT(KSCHED2,DEBUGPRINT("ReadyT->CPU %d",cpu)); + TSubScheduler& ss = TheSubSchedulers[cpu]; + ss.iReadyListLock.LockOnly(); + TInt hp = ss.HighestPriority(); + if (g->iPriority>hp || (g->iPriority==hp && ss.iCurrentThread && ss.iCurrentThread->iTime==0)) + { + if (&ss == &ss0) + RescheduleNeeded(); // reschedule on this processor + else + ss0.iReschedIPIs |= ss.iCpuMask; // will kick the other CPU when this CPU reenables preemption + } + ss.Add(g); + g->iReady = TUint8(cpu | EReadyOffset); + if ((aMode & ENewTimeslice) && iParent && t->iTime==0 && g->iNext!=g) + t->iTime = t->iTimeslice; + ss.iReadyListLock.UnlockOnly(); + } + + +NThread* TSubScheduler::SelectNextThread() + { + NThread* ot = iCurrentThread; + NThread* t = 0; + TBool migrate = FALSE; + TBool gmigrate = FALSE; + TBool fmd_done = FALSE; + TBool fmd_res = FALSE; + if (!ot) + { + iReadyListLock.LockOnly(); + iRescheduleNeededFlag = FALSE; + goto no_ot; + } + ot->AcqSLock(); + if (ot->iNewParent) + ot->iNewParent->AcqSLock(); + SaveTimesliceTimer(ot); // remember how much of current thread's timeslice remains + if (ot->iCsFunction==NThreadBase::ECSDivertPending && ot->iWaitState.iWtC.iWtStFlags) + { + // thread about to exit so cancel outstanding wait + ot->DoReleaseT(KErrDied,0); + } + if (ot->iWaitState.iWtC.iWtStFlags==0) + { + // ASSUMPTION: If iNewParent set, ot can't hold a fast mutex (assertion in JoinGroup) + TBool pfmd = (ot->iParent!=ot && !ot->iFastMutexDefer); + if (ot->iTime==0 || pfmd) + { + // ot's timeslice has expired + fmd_res = ot->CheckFastMutexDefer(); + fmd_done = TRUE; + if (fmd_res) + { + if (ot->iTime == 0) + ot->iTime = 0x80000000; // mark deferred timeslice expiry + if (pfmd) + { + ot->iFastMutexDefer = 1; + ++ot->iParent->iFreezeCpu; + } + } + } + } + iReadyListLock.LockOnly(); + iRescheduleNeededFlag = FALSE; + + // process outstanding suspend/kill/CPU change on ot + + __NK_ASSERT_DEBUG(!(ot->iWaitState.iWtC.iWtStFlags & NThreadWaitState::EWtStWaitActive)); + if (ot->iWaitState.iWtC.iWtStFlags || ot->iPauseCount || ot->iSuspended) + { + // ot is no longer ready to run + __KTRACE_OPT(KSCHED2,DEBUGPRINT("Rschd<-%T WS: %02x %02x (%08x) P:%02x S:%1x", ot, + ot->iWaitState.iWtC.iWtStFlags, ot->iWaitState.iWtC.iWtObjType, ot->iWaitState.iWtC.iWtObj, ot->iPauseCount, ot->iSuspended)); + TInt wtst = ot->iWaitState.DoWait(); + if (wtst>=0 && wtst!=NThread::EWaitFastMutex) + ot->iTime = ot->iTimeslice; + ot->UnReadyT(); + if (ot->iNewParent) + { + ot->iParent = ot->iNewParent, ++((NThreadGroup*)ot->iParent)->iThreadCount; + wmb(); // must make sure iParent is updated before iNewParent is cleared + ot->iNewParent = 0; + } + ot->iCpuChange = FALSE; + } + else if (ot->iNewParent) + { + __NK_ASSERT_ALWAYS(ot->iParent==ot && !ot->iHeldFastMutex && !ot->iFreezeCpu); + ot->UnReadyT(); + migrate = TRUE; + ot->iParent = ot->iNewParent; + ot->iCpuChange = FALSE; + ++((NThreadGroup*)ot->iParent)->iThreadCount; + wmb(); // must make sure iParent is updated before iNewParent is cleared + ot->iNewParent = 0; + } + else if (ot->iParent->iCpuChange && !ot->iParent->iFreezeCpu) + { + if (!CheckCpuAgainstAffinity(iCpuNum, ot->iParent->iCpuAffinity)) + { + if (ot->iParent==ot) + { + if (!fmd_done) + fmd_res = ot->CheckFastMutexDefer(), fmd_done = TRUE; + if (!fmd_res) + { + __KTRACE_OPT(KSCHED2,DEBUGPRINT("Rschd<-%T A:%08x",ot,ot->iParent->iCpuAffinity)); + ot->UnReadyT(); + migrate = TRUE; + ot->iCpuChange = FALSE; + } + } + else + { + __KTRACE_OPT(KSCHED2,DEBUGPRINT("Rschd<-%T GA:%08x",ot,ot->iParent->iCpuAffinity)); + Remove(ot->iParent); + ot->iParent->iReady = 0; + gmigrate = TRUE; + ot->iCpuChange = FALSE; + ot->iParent->iCpuChange = FALSE; + } + } + else + { + ot->iCpuChange = FALSE; + ot->iParent->iCpuChange = FALSE; + } + } +no_ot: + NSchedulable* g = (NSchedulable*)First(); + TBool rrcg = FALSE; + if (g && g->IsGroup()) + { + t = (NThread*)((NThreadGroup*)g)->iNThreadList.First(); + if (g->iNext!=g) + rrcg = TRUE; + } + else + t = (NThread*)g; + TBool rrct = (t && t->iNext!=t); + if (t && t->iTime==0 && (rrcg || rrct)) + { + // candidate thread's timeslice has expired and there is another at the same priority + if (t==ot) + { + if (ot->iParent!=ot) + { + ((NThreadGroup*)ot->iParent)->iNThreadList.iQueue[ot->iPriority] = ot->iNext; + iQueue[ot->iParent->iPriority] = ot->iParent->iNext; + } + else + iQueue[ot->iPriority] = ot->iNext; + ot->iTime = ot->iTimeslice; + NSchedulable* g2 = (NSchedulable*)First(); + if (g2->IsGroup()) + t = (NThread*)((NThreadGroup*)g2)->iNThreadList.First(); + else + t = (NThread*)g2; + if (t->iTime==0) + { + // loop again since we need to lock t before round robining it + __KTRACE_OPT(KSCHED2,DEBUGPRINT("Rschd<-%T RRL",ot)); + iRescheduleNeededFlag = TRUE; + } + else + { + __KTRACE_OPT(KSCHED2,DEBUGPRINT("Rschd<-%T RR",ot)); + } +/* if (ot->iCpuAffinity & NTHREADBASE_CPU_AFFINITY_MASK) + { + ot->UnReadyT(); + migrate = TRUE; + } + else + ot->iTime = ot->iTimeslice; +*/ + } + else // loop again since we need to lock t before round robining it + { + __KTRACE_OPT(KSCHED2,DEBUGPRINT("Rschd<-%T LL",ot)); + iRescheduleNeededFlag = TRUE; + } + } + if (t != ot) + { + if (ot) + { + ot->iCurrent = 0; + ot->iParent->iCurrent = 0; + ot->CompleteContextSave(); + } + if (t) + { + t->iLastCpu = iCpuNum; + t->iParent->iLastCpu = iCpuNum; + t->iCurrent = TUint8(iCpuNum | NSchedulable::EReadyOffset); + t->iParent->iCurrent = t->iCurrent; + } + iCurrentThread = t; + } + UpdateThreadTimes(ot,t); // update ot's run time and set up the timeslice timer for t + iReadyListLock.UnlockOnly(); + if (migrate) + ot->ReadyT(NThreadBase::ENewTimeslice); // new timeslice if it's queued behind another thread at same priority + if (gmigrate) + ot->iParent->ReadyT(0); // new timeslice if it's queued behind another thread at same priority + if (ot) + { + ot->RelSLock(); + + // DFC to signal thread is now dead + if (ot->iWaitState.ThreadIsDead() && ot->iWaitState.iWtC.iKillDfc) + ot->iWaitState.iWtC.iKillDfc->DoEnque(); + } + __KTRACE_OPT(KSCHED,DEBUGPRINT("Rschd->%T",t)); + __NK_ASSERT_ALWAYS(!t || t->iParent); // must be a thread not a group + return t; // could return NULL + } + + +void NThreadBase::UnReadyT() + { + if (iParent!=this) + { + NThreadGroup& g = *(NThreadGroup*)iParent; + TPriListBase& l = g.iNThreadList; + l.Remove(this); + if (g.iReady) + { + TSubScheduler& ss = TheSubSchedulers[g.iReady & EReadyCpuMask]; + if (l.IsEmpty()) + { +// __KTRACE_OPT(KNKERN,DEBUGPRINT("%T UnReadyT (G=%G-)",this,&g)); + ss.Remove(&g); + g.iReady = 0; + g.iPriority = 0; + } + else + { +// __KTRACE_OPT(KNKERN,DEBUGPRINT("%T UnReadyT (G=%G)",this,&g)); + ss.ChangePriority(&g, l.HighestPriority()); + } + } + } + else + { +// __KTRACE_OPT(KNKERN,DEBUGPRINT("%T UnReadyT",this)); + TheSubSchedulers[iReady & EReadyCpuMask].Remove(this); + } + iReady = 0; + } + + +void NThreadBase::ChangeReadyThreadPriority() + { + TInt newp = iMutexPri>iBasePri ? iMutexPri : iBasePri; + TInt oldp = iPriority; + TSubScheduler* ss0 = &SubScheduler(); + TSubScheduler* ss = 0; + if (iParent->iReady) + { + ss = TheSubSchedulers + (iParent->iReady & EReadyCpuMask); + ss->iReadyListLock.LockOnly(); + } + TBool resched = FALSE; + NSchedulable* g = iParent; + if (g!=this) + { + NThreadGroup* tg = (NThreadGroup*)g; + tg->iNThreadList.ChangePriority(this, newp); + if (ss) + { + TInt ngp = tg->iNThreadList.HighestPriority(); + if (ngp!=tg->iPriority) + ss->ChangePriority(tg, ngp); + } + } + else + ss->ChangePriority(this, newp); + if (iCurrent) // can't be current if parent not ready + { + TInt nhp = ss->HighestPriority(); + if (newpiCurrentThread; + TInt cp = ct ? ct->iPriority : -1; + if (newp>cp || (newp==cp && ct->iTime==0)) + resched = TRUE; + } + if (resched) + { + if (ss == ss0) + RescheduleNeeded(); + else + ss0->iReschedIPIs |= ss->iCpuMask; // will kick the other CPU when this CPU reenables preemption + } + if (ss) + ss->iReadyListLock.UnlockOnly(); + } + + +/** Changes the priority of a nanokernel thread. + + For use by RTOS personality layers. + Do not use this function directly on a Symbian OS thread. + + The thread's unknown state handler will be invoked with function EChangePriority + and parameter newp if the current NState is not recognised and the new priority + is not equal to the original priority. + + @param newp The new nanokernel priority (0 <= newp < KNumPriorities). + + @pre Kernel must be locked. + @pre Call in a thread context. + + @post Kernel is locked. + */ +EXPORT_C void NThreadBase::SetPriority(TInt newp) + { + CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_IDFC|MASK_NOT_ISR,"NThreadBase::SetPriority"); + AcqSLock(); + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T nSetPri %d(%d)->%d(%d)",this,iPriority,iBasePri,newp,iMutexPri)); + iBasePri = TUint8(newp); + if (iMutexPri > iBasePri) + newp = iMutexPri; + TInt oldp = iPriority; + if (newp == oldp) + { + RelSLock(); + return; + } + NFastMutex* wfm = 0; + if (iLinkedObj && iLinkedObjType==EWaitFastMutex) + wfm = (NFastMutex*)iLinkedObj; + if (wfm) + { + // if thread is attached to/waiting on a fast mutex, need to acquire mutex lock + ++iPauseCount; + RelSLock(); + wfm->iMutexLock.LockOnly(); + AcqSLock(); + UnPauseT(); + wfm->iWaitQ.ChangePriority(&iWaitLink, newp); // change position of this thread on mutex wait queue + } + if (iReady) + { + ChangeReadyThreadPriority(); + RelSLock(); + if (wfm && newp<=wfm->iWaitQ.HighestPriority()) + { + // this thread was contending for the mutex but they may be other waiting threads + // with higher or equal priority, so wake up the first thread on the list. + NThreadBase* pT = _LOFF(wfm->iWaitQ.First(), NThreadBase, iWaitLink); + pT->AcqSLock(); + + // if thread is still blocked on this fast mutex, release it but leave it on the wait queue + // NOTE: it can't be suspended + pT->iWaitState.UnBlockT(NThreadBase::EWaitFastMutex, wfm, KErrNone); + pT->RelSLock(); + } + } + else + { + iPriority = (TUint8)newp; + if (wfm && newp>oldp) + { + NThreadBase* pT = _LOFF(wfm->iWaitQ.First(), NThreadBase, iWaitLink); // highest priority waiting thread + if (pT==this) + { + // this is now highest priority waiting thread so wake it up + iWaitState.UnBlockT(NThreadBase::EWaitFastMutex, wfm, KErrNone); + } + } + RelSLock(); + } + if (wfm) + { + NThreadBase* t = (NThreadBase*)(TLinAddr(wfm->iHoldingThread)&~3); + if (t) + t->SetMutexPriority(wfm); + wfm->iMutexLock.UnlockOnly(); + } + } + + +/** Set the inherited priority of a nanokernel thread. + + @pre Kernel must be locked. + @pre Call in a thread context. + @pre The thread holds a fast mutex + + @post Kernel is locked. + */ +void NThreadBase::SetMutexPriority(NFastMutex* aM) + { + TInt newp = aM->iWaitQ.HighestPriority(); + if (newp<0) + newp = 0; + AcqSLock(); + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T nSetMPri %d->%d Base %d (mutex %08x)",this,iMutexPri,newp,iBasePri,aM)); + iMutexPri = TUint8(newp); + if (iMutexPri < iBasePri) + newp = iBasePri; + TInt oldp = iPriority; + if (newp == oldp) + { + RelSLock(); + return; + } + if (iReady) + ChangeReadyThreadPriority(); + else + iPriority = (TUint8)newp; + RelSLock(); + } + + +void NThreadBase::LoseInheritedPriorityT() + { + __KTRACE_OPT(KNKERN,DEBUGPRINT("%T nLoseInhPri %d->%d",this,iPriority,iBasePri)); + TSubScheduler* ss = &SubScheduler(); + TInt newp = iBasePri; + NSchedulable* g = iParent; + ss->iReadyListLock.LockOnly(); + if (g!=this) + { + NThreadGroup* tg = (NThreadGroup*)g; + tg->iNThreadList.ChangePriority(this, newp); + TInt hp = tg->iNThreadList.HighestPriority(); + if (hp == tg->iPriority) + { + if (newp <= hp) + RescheduleNeeded(); + goto out; + } + newp = hp; + g = tg; + } + if (newp <= ss->HighestPriority()) + RescheduleNeeded(); + ss->ChangePriority(g, newp); +out: + ss->iReadyListLock.UnlockOnly(); + } + +