kernel/eka/nkernsmp/sched.cpp
changeset 0 a41df078684a
child 90 947f0dc9f7a8
child 256 c1f20ce4abcf
--- /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();
+	}
+
+