Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 25
Текст из файла (страница 25)
By an IDFC3. By the timer tick interrupt, if the current thread’s timeslice has expired.If the flag is clear, no further action is needed and the scheduler returns.If it is set, we proceed to switch threads.Lines 11–14 are straightforward: we can re-enable interrupts at thispoint since the most they could do is queue another IDFC that wouldeventually cause the scheduler to loop. We save the current thread’sregister context on the stack, which is the thread’s own supervisor modestack. Then we store the stack pointer in the current thread’s iSavedSPfield and clear iRescheduleNeededFlag since we are about to doa reschedule.Line 15 implements the basic scheduling policy.
The most significantbit in the 64-bit mask indicates the highest priority of any ready thread.We select the first thread on the queue corresponding to that priority as acandidate to run.Lines 16–25 deal with round-robin scheduling of threads at the samepriority. The system tick interrupt decrements the current thread’s iTimefield; when this reaches zero the thread’s timeslice has expired, so theiRescheduleNeededFlag is set, which causes a reschedule at thenext possible point – either at the end of the tick ISR or when the kernelis next unlocked. Line 16 checks to see if the selected thread’s timeslicehas expired.
If it has, and there is another thread at the same priority,and the originally selected thread does not hold a fast mutex, then we104THREADS, PROCESSES AND LIBRARIESselect the next thread in round-robin order and we reset the originalthread’s timeslice.If the original thread does hold a fast mutex, we defer the round-robinand set the fast mutex iWaiting flag so that the round-robin will betriggered when the thread releases the mutex.
We defer the round-robinto reduce the time that might be wasted by context switching to anotherthread that then immediately waits on the same mutex and causes anothercontext switch. This would be a particular problem with threads waitingon the system lock. We expect that a fast mutex will only be held for shortperiods at a time and so the overall pattern of round-robin schedulingwill not be disturbed to any great extent.Lines 26–36 deal with the case where the selected thread holds a fastmutex. If the thread holds the system lock, we can simply switch straightto the thread with no further checking, since the address space cannothave been changed since the thread last ran. Also, the thread cannotbe blocked on another fast mutex (because it holds one and they donot nest).If the selected thread holds a fast mutex other than the system lock, westill switch to it, and we don’t have to call out to do address space changes,since we don’t guarantee that the user-mode address space is valid duringa critical section protected by a fast mutex (unless it’s the system lock).However, if an address space change would normally be required, we setthe mutex iWaiting flag to ensure that the address space change doesactually occur when the fast mutex is released.
In addition, if the threadhas the KThreadAttImplicitSystemLock attribute and the systemlock is currently held, we set the mutex iWaiting flag. This is to ensurethat the thread doesn’t exit the mutex-protected critical section while thesystem lock is held.Lines 37–42 deal with the case where the selected thread is actuallyblocked on a fast mutex. Such threads stay on the ready list, so the kernelmay select them during a reschedule. We do not want to waste time byswitching to the thread and letting it run, only to immediately switch tothe holding thread. So we check for this case in the scheduler and gostraight to the mutex holding thread, thus saving a context switch.
Thischeck also guarantees that the YieldTo function used in NFastMutexwait operations cannot return until the mutex has been released. Noticethat we need to check both iWaitFastMutex and iWaitFastMutex>iHoldingThread, since when the holding thread releases the mutex,iHoldingThread will be set to NULL but iWaitFastMutex will stillpoint to the mutex. As before, there is no need to do any address spacechanging if we switch to the mutex holding thread. There is also no needto set the fast mutex iWaiting flag here since it must already have beenset when the selected thread blocked on it.Lines 43–50 deal with threads requiring an implicit system lock. Wemainly use this mechanism for threads requiring long-running addressSCHEDULING105space switches: to perform such a switch the scheduler must claim thesystem lock.
Threads that do not need implicit system lock will also notneed the scheduler to call the address-space-switch hook; the schedulercan simply switch to them at this point (lines 43–45). If the selectedthread does require an implicit system lock, the scheduler then checksif the lock is free. If it is not, the scheduler switches to the system lockholding thread. It also sets the system lock’s iWaiting flag since thereis now a thread implicitly waiting on the system lock.If we reach line 51, the selected thread needs an implicit system lockand the lock is free.
If the thread does not need an address space change,we can now switch it in – the system lock is not claimed (lines 51–53).If the thread does require an address space change – that is, it has theKThreadAttAddressSpace attribute and its iAddressSpace valuediffers from the currently active one – then the scheduler calls out to dothis address space change (line 60).In lines 54–56 we do the actual thread switch. We change stacksand restore the CPU-specific registers from the new thread’s stack.
Atthis point, the new thread is effectively running with the kernel locked.Lines 57–58 claim the system lock for the new thread – we know thatit’s free here. Then we unlock the kernel (line 59). From this point on,further preemption can occur. It’s also worth noting that the schedulerwill go recursive here if an IDFC was queued by an interrupt servicedduring the first part of the reschedule. There can only be one recursion,however, since the second reschedule would find the system lock heldand so could not reach the same point in the code.Line 60 calls the address space switch handler in the Symbian OSmemory model to perform any MMU page table manipulations requiredto change to the required address space.
The switch hander is alsoresponsible for changing the iAddressSpace field in the scheduler toreflect the new situation. Since the switch handler runs with the kernelunlocked, it does not directly affect thread latency. But it does affectlatency indirectly, since most Symbian OS user mode threads need anaddress space switch to run and many kernel functions wait on the systemlock. The switch handler also affects the predictability of execution timefor Symbian OS kernel functions.
This means that we want the systemlock to only be held for a very short time. To accommodate this, theaddress space switch handler does the operation in stages and checksthe system lock’s iWaiting flag after each stage. If the flag is set, theswitch handler simply returns and we trigger a further reschedule (line 64)to allow the higher-priority thread to run.
We set the iAddressSpacefield in the scheduler to NULL just before we make the first change to theaddress space and we set it to the value corresponding to the new threadjust after the last change is made. This ensures that we take the correctaction if the address space switch handler is preempted and anotherreschedule occurs in the middle of it.106THREADS, PROCESSES AND LIBRARIESAfter we’ve done the address space switch, we lock the kernel again andrelease the system lock (lines 61–70). If contention occurred for the lock,we do not call the scheduler again directly as would normally be the casein a fast mutex signal; instead we set the iRescheduleNeededFlag,which will cause the scheduler to loop. Attempting recursion at this pointwould be incorrect, because the system lock is now free and there would benothing to limit the recursion depth. [The processing of a deferred criticalsection operation (line 68) could cause the scheduler to go recursive, butonly in the case where the thread was exiting; clearly, this cannot occurmore than once.
A deferred suspension would simply remove the threadfrom the ready list and set the iRescheduleNeededFlag, which wouldthen cause the scheduler to loop.]Lines 73–75 cover the case in which an address space switch wasnot required. They do the actual thread switch by switching to the newthread’s stack and restoring the CPU-specific registers.In lines 76 and onwards we finish the reschedule. First we restore thenon-volatile registers, then we disable interrupts and make a final check ofiDfcPendingFlag and iRescheduleNeededFlag. We need to dothis because interrupts have been enabled for most of the reschedule andthese could either have queued an IDFC or expired the current thread’stimeslice.
Furthermore, the iRescheduleNeededFlag may have beenset because of system lock contention during the processing of an addressspace change. In any case, if either of these flags is set, we loop rightback to the beginning to run IDFCs and/or select a new thread. If neitherof the flags is set, then we unlock the kernel and exit from the schedulerin the context of the new thread.The scheduler always returns with interrupts disabled.
We need tomake sure of this to prevent unbounded stack usage because of repeatedinterrupts in the same thread. When an interrupt occurs, the kernel pushesthe volatile registers (that is, those modified by a normal function call)onto the thread’s supervisor stack before calling the scheduler. If interrupts were re-enabled between unlocking the kernel and popping theseregisters, another interrupt could occur and push the volatile registersagain before causing another reschedule.
And so it might continue, ifinterrupts were not disabled.The algorithm and the explanation above apply to the moving memorymodel. The emulator and direct memory models do not do any addressspace switching, simplifying scheduling. The multiple memory modeluses a simplified address space switching scheme, since switching is veryfast. The multiple memory model scheduling algorithm becomes:////123Enter with kernel lockedActive stack is supervisor stack of current threadDisable interruptsstart_reschedule:IF IDFCs pending (iDfcPendingFlag TRUE)SCHEDULING45678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758Run IDFCs (with interrupts enabled but kernel locked)iDfcPendingFlag = FALSEENDIFIF reschedule not needed (iRescheduleNeededFlag FALSE)iKernCSLocked=0 (unlock the kernel)ReturnENDIFReenable interruptsSave non-volatile registers and CPU specific registers on stackiCurrentThread->iSavedSP = current stack pointeriRescheduleNeededFlag = FALSEnext_thread = first thread in highest priority non-empty readyqueueIF next_thread->iTime==0 (ie timeslice expired)IF another thread is ready at same priority as next_threadIF next_thread holds a fast mutexnext_thread->iHeldFastMutex->iWaiting=TRUEgoto resched_endENDIFnext_thread->iTime=next_thread->iTimeslice (new timeslice)next_thread=thread after next_thread in round-robin orderENDIFENDIFIF next_thread holds a fast mutexIF next_thread holds system lockgoto resched_endELSE IF next_thread requires implicit system lock and systemlock heldnext_thread->iHeldFastMutex->iWaiting=TRUEgoto resched_endENDIFENDIFIF next_thread is blocked on a fast mutexIF next_thread->iWaitFastMutex->iHoldingThread (mutex is stilllocked)next_thread=next_thread->iWaitFastMutex->iHoldingThreadgoto resched_endENDIFENDIFIF next_thread does not require implicit system lockgoto resched_endELSE IF system lock held by another threadnext_thread=system lock holding threadsystem lock iWaiting = TRUEENDIFresched_end:iCurrentThread=next_threadcurrent stack pointer = next_thread->iSavedSPswitch_threads:Restore CPU specific registers from stackIF next_thread requires address space switchInvoke address space switch handler (kernel still locked)ENDIFRestore nonvolatile registers from stackDisable interruptsIF IDFCs pending or another reschedule neededgoto start_reschedENDIF1071085960THREADS, PROCESSES AND LIBRARIESiKernCSLocked=0 (unlock the kernel)Return with interrupts disabledThe main difference here is that we can invoke the address space switchhandler with preemption disabled.