Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 16
Текст из файла (страница 16)
The practicalconsequence of this is that these threads don’t need to enter criticalsections – so if you were to kill them at the wrong point, you would havethe problems outlined in Section 3.2.4.1.If the subject thread is in a critical section, then the kernel will notkill it immediately, but will mark it ‘‘exit pending’’ and kill it once it hasexited its critical section.In either case, the exiting thread first invokes its exit handler, whichI’ll discuss next.3.2.5.1 Exit handlerAn exiting thread will invoke its exit handler (iExitHandler) if oneexists.
The exit handler runs with the kernel unlocked and the exitingthread in a critical section, so it is impossible to suspend the thread whilerunning its exit handler.The exit handler can return a pointer to a DFC (deferred function call)that will be queued just before the thread actually terminates, that is justbefore the kernel sets the thread’s iNState to EDead. The Symbian OSkernel also uses the DFC to perform final cleanup after the thread hasterminated – for example to delete the thread control block.3.2.6 Threads in the emulatorThe Win32 NThread class has the following members to add to those ofthe generic NThreadBase:FieldiWinThreadDescriptionWin32 handle that refers to the host thread that underlies thisthread.iScheduleLock Win32 event object used to block the host thread when notscheduled by the nanokernel.
Every nanokernel thread except thecurrent thread will either be suspended in the host OS, in whichcase iWakeup is EResume or EResumeLocked, otherwise itwill be waiting on this event object in its control block.60THREADS, PROCESSES AND LIBRARIESiDivertA function pointer used to divert a thread from normal returnfrom the scheduler. Diverting threads using a forced change ofcontext in the host OS is unsafe and is only used for forcedtermination of a thread that is suspended.iInKernelA counter used to determine if the thread is in ‘‘kernel’’ mode.
Azero value indicates ‘‘user’’ mode. This state is analogous tobeing in supervisor mode on ARM – it is used primarily todetermine the course of action following an exception.iWakeupRecords the method required to resume the thread when itbecomes the current thread.3.2.6.1 Thread creationInstantiating a thread in the emulator is a little more complex than ona target phone, as we must create and initialize a host thread, acquirethe resources to control its scheduling, and hand the initial data overto the thread. On the target, we hand the initial data over by directlymanipulating the new thread stack before it runs; on the emulator this isbest avoided as it involves directly manipulating the context of a live hostthread.
The nanokernel API allows an arbitrary block of data to be passedto the new thread. Because the new thread does not run synchronouslywith the create call, but rather when the new thread is next scheduled torun, we must hand over this data before the creating thread returns fromNThread::Create().When creating a new nanokernel thread in the emulator, we create anevent object for the reschedule lock, then create a host OS thread to runthe NThread::StartThread() method, passing an SCreateThreadparameter block.
The parameter block contains a reference to the threadcreation information and a fast mutex used to synchronize the datablock handover. The handover requires that the new thread runs tocopy the data, but it must be stopped again straight afterwards, becausewhen NThread::Create() returns the new thread must be left ina NThread::ESuspended state. Now I’ll describe in detail how weachieve this.We create the new Windows thread, leaving it suspended within Windows. NThread::Create() then marks the new thread as preempted(within the nanokernel) so that it will be resumed when the nanokernelschedules it to run. The creating thread then locks the kernel, sets upthe handover mutex so that it is held by the new thread, and sets adeferred suspension on the new thread.
(The deferred suspension is notimmediately effective as the new thread holds a fast mutex.) When thecreating thread waits on the same fast mutex, the scheduler runs the newthread from its entry point.NANOKERNEL THREADS61The new thread now initializes. It sets an exception handler for theWindows thread so that the emulator can manage access violations, andthen copies the data out of the parameter block. Initialization is thencomplete so the new thread signals the handover mutex.
This actionactivates the deferred suspension, leaving the new thread suspended asrequired. Signaling the mutex also wakes up the creating thread, whichwas blocked on the mutex. The creating thread can now release themutex again and return, as the new thread is ready to run.When the new thread is resumed, it invokes the supplied thread entrypoint function and then exits if that returns.3.2.6.2 Thread exitOn target, a nanothread is considered dead when its state is NThread::EDead and after it enters TScheduler::Reschedule for the last timeand schedules another thread to run. Because of its state, it will never bescheduled to run again, and its control block can be destroyed.If the emulator followed this model directly, the Windows thread wouldbe left blocked on its iRescheduleLock.
Although the host thread andevent object could then be discarded when the thread’s control block isdestroyed by calling the TerminateThread() function (as in EKA1),Windows does not recommend such use of this function.Instead, we want the Windows thread to exit cleanly by calling theExitThread() function.
To do this, we ensure that we handle the finalthread switch (detected by checking a thread state of EDead) slightlydifferently. We wake up the next Windows thread to run but do not blockthe dying thread. Instead, the dying thread releases the thread and eventhandles before calling ExitThread().3.2.6.3 Forced exit – diverting threadsWhen one thread kills another, the victim has to be diverted from itscurrent activity into the thread exit processing.
As we saw before, doingsuch a diversion by making a forced change of context in Windows candestabilize Windows itself.The vast majority of situations in which this occurs involve a thread thathas voluntarily rescheduled, by calling TScheduler::Reschedule(),rather than being preempted, due to an interrupt. In these situations we candivert safely by making the return path from TScheduler::Reschedule() check to see if a diversion is needed.
We use theNThread::iDivert member to record the existence of a diversion onthe thread.This only leaves the rare case where the thread has been preempted,in which case diversion has to involve changing the Windows threadcontext – there is no other alternative.62THREADS, PROCESSES AND LIBRARIESThis doesn’t crash Windows for two reasons:1.We don’t run the emulator on versions of Windows that suffer likethis (such as Windows 98)2.We ensure that we don’t kill the thread when it is executing codeinside a Windows ‘‘kernel’’ function.Also we recommend that any threads interacting with Windows shoulduse Emulator::Lock() and Unlock() to prevent preemption whilst‘‘inside’’ Windows.3.3 Symbian OS threadsThe Symbian OS kernel builds on nanothreads to provide support for usermode threads that are used by standard Symbian OS user applications.We represent these threads using the DThread class.Earlier in this chapter, I said that nanothreads always execute insupervisor mode and have a single supervisor-mode stack.
Each SymbianOS thread object contains a nanothread object; this nanothread is whatthe kernel actually schedules. The nanokernel cannot allocate memory,so when it is creating a Symbian OS thread, the Symbian OS kernel mustallocate the memory for the nanothread’s supervisor stack before it makesthe ThreadCreate() call to the nanokernel.Each user-mode Symbian OS thread naturally has a user-mode stack,so, to spell it out, user-mode Symbian OS threads each have two stacks(Figure 3.1). The supervisor-mode stack associated with the nanothread isused by kernel-side code run in the thread’s context – that is, the systemcalls made by the thread.The use of per-thread supervisor stacks is a fundamental difference fromEKA1, in which each thread had only one stack. By adding the supervisorstack to each thread, we allow kernel-side code to be preempted andthus achieve low thread latencies.