--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/nkernsmp/sched.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -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 <nk_irq.h>
+
+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; i<KMaxCpus; ++i)
+ {
+ TSubScheduler* s = TheSubSchedulers + i;
+ iSub[i] = s;
+ s->iScheduler = this;
+ s->iCpuNum = TUint32(i);
+ s->iCpuMask = 1u<<i;
+ }
+ }
+
+
+/** Return a pointer to the scheduler
+ Intended for use by the crash debugger, not for general device driver use.
+
+ @return Pointer to the scheduler object
+ @internalTechnology
+ */
+EXPORT_C TScheduler* TScheduler::Ptr()
+ {
+ return &TheScheduler;
+ }
+
+
+/******************************************************************************
+ * TSubScheduler
+ ******************************************************************************/
+
+// TSubScheduler resides in .bss so other fields are zero-initialised
+TSubScheduler::TSubScheduler()
+ : TPriListBase(KNumPriorities),
+ iExIDfcLock(TSpinLock::EOrderExIDfcQ),
+ iReadyListLock(TSpinLock::EOrderReadyList),
+ iKernLockCount(1),
+ iEventHandlerLock(TSpinLock::EOrderEventHandlerList)
+ {
+ }
+
+
+/******************************************************************************
+ * NSchedulable
+ ******************************************************************************/
+void NSchedulable::AcqSLock()
+ {
+ iSSpinLock.LockOnly();
+ if (iParent!=this && iParent)
+ iParent->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<s.iNumCpus; ++i)
+ {
+ TSubScheduler& ss = *s.iSub[i];
+ if (!(m & ss.iCpuMask))
+ continue;
+ TInt hp = ss.HighestPriority();
+ if (hp < lowest_p)
+ {
+ lowest_p = hp;
+ cpu = i;
+ continue;
+ }
+ if (hp > 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 (newp<oldp && (newp<nhp || (newp==nhp && iTime==0)))
+ resched = TRUE;
+ }
+ else if (ss)
+ {
+ NThreadBase* ct = ss->iCurrentThread;
+ 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();
+ }
+
+