Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 31
Текст из файла (страница 31)
Here, we try to give a flavorof how they work and why they are important.6.1 The Asynchronous ServiceAt the heart of what we call event-driven programming is the concept ofthe asynchronous service.When a program requests a service of some other component orpart of the system, that service can be performed either synchronouslyor asynchronously. A synchronous service is the ‘usual’ pattern forfunction calls; when the function returns, the service requested has eitherbeen performed or some kind of error has been returned.
The programthen continues executing based upon the outcome of the synchronousoperation.An asynchronous service is requested by a function call, but completion occurs later and is indicated by some kind of signal. In this way, the152ACTIVE OBJECTSprogram flow is split into two entities. First, the operation is requestedand then later, when the outcome of the operation is known, the programcontinues execution based upon this outcome.It is normal, though not mandatory, that the requesting entity is partitioned in some way from the component that implements the service beingrequested, known as the service provider. For example, the requestingcomponent and service provider may be in different threads.Between the issue of the request and the receipt of the signal, therequest is said to be pending.
The requesting program may do otherprocessing while the request is pending but until the signal is receivedthe requested operation cannot be assumed to have been completed.Therefore, ultimately the requesting entity waits, or sleeps, until the signalis received. The operating system wakes up the requesting entity whencompletion of its pending request is signaled.This is the crux of a typical event-driven program; it makes requests forkeyboard input, for pen events, timer events, etc. and, once it has nothingelse to do, it sits and waits. When an event occurs, the program comes outof its wait state, responds to the event in whatever manner is appropriate,typically issues a request for another similar event, and then waits again.Figure 6.1 shows the typical pattern in a simplistic way.
Ignoring startup, shut down, and many other issues, there’s a central core that waits;when an event occurs, it calls the code that can deal with that event;control then flows back to the central core.Consider Figure 6.1 representing code running in a single thread. Ifonly one event-handling section of code can run at any one time, suchas the code that runs when handling a key event, and whilst that codeCode that runswhen a keyevent occursAn eventcompletion issignalledWait CoreCode that runswhen a timerevent occursCode that runswhen a penevent occursFigure 6.1Overview of an event-driven programTHE ASYNCHRONOUS SERVICE153is running nothing else in that thread can run until control flows back tothe central wait core, then this represents non-pre-emptive multitaskingwithin the context of a single thread.To help illustrate these concepts further, let’s consider a simple use casewhere the thread-event handling is implemented in a non-pre-emptivemultitasking way.
The use case describes a program that requests to beinformed when:• a keypress event occurs• a ten-minute timer expires.Specific actions might be carried out when these events are triggered,such as updating the screen or playing a sound.There are four key actors involved in the use case: a keyboard object,a timer object, an initialization object and a wait object.Let’s consider the key roles of each actor in turn:• the Initialization object represents the logic associated withthe preparatory stage of the program; it controls the creation andinitialization of the other objects• the Timer object understands how to request that an event occur ata specific time; it wants to perform some action when the timer eventoccurs• the Keyboard object understands how to request an event when theuser presses a key on the keyboard; it wants to be able to performsome action, for example, updating the display when a key is pressed• the Waiter object is responsible for detecting when an event hasoccurred and then ensuring that any resultant action required tohandle the event takes place.Figure 6.2 represents the execution of the program.Initialization Phase1.
At the beginning, the initialization code prepares the other objects tocarry out the use case.2. Firstly, the initializer instructs the keyboard object to make anasynchronous request to receive any future key events. After theasynchronous request is issued, the keyboard object’s request ispending.3. The control returns to the initialization object.
Since requests areasynchronous, the initialization code can continue to interact withother objects.154ACTIVE OBJECTSFigure 6.2 An event-driven program4.Next, a call is made to prepare the timer object. The timer requeststhat an event occurs in ten minutes. Again, the timer’s requestbecomes pending.Waiting Phase 15.The objects have been prepared to receive the events when theyoccur. The program now enters the waiting phase.
In this state, theTHE ASYNCHRONOUS SERVICE155initialization object finishes its task by requesting that the waiterobject start to wait for event-completion signals. At this point, theprogram is now truly asynchronously event-driven. It is waiting forsome external input, such as a keypress to continue execution of theprogram’s code.The Keyboard Event Dispatch Phase6.In this example, we can see that a key event occurs at the two-minutemark.
The act of completing the keyboard object’s asynchronousrequest moves its status from pending to completed, as representedby the C symbol.7.At this point, the wait object is signaled that an asynchronousrequest has been completed and therefore it awakens.8.The waiter object is able to identify that at a keyboard event hasoccurred and therefore it activates the keyboard object to processthe event.9.After the keyboard object has processed the event, it makes anotherasynchronous request to receive any future key events. Again itsrequest becomes pending, represented by the P symbol.Waiting Phase 210.Now that the key event has been fully processed, program executionreturns to the waiter object. Again, it begins to wait for the signalthat another request has been completed.The Timer Event Dispatch Phase11.At the tenth minute of execution, the timer event occurs. The timerobject’s status changes from pending to completed.12.The waiter is signaled about the event.
This causes it to awaken.13.The waiter detects the completion of the timer event and actsaccordingly, asking it to handle the event.14.The timer object processes the event, perhaps playing a tone.Waiting Phase 315.Again, since the event has been fully handled, the timer returnsprogram execution control to the waiter. It waits for a signal thatanother request has been completed.The above logic illustrates several of the key concepts implementedby Symbian’s active object framework. The code that runs when an event156ACTIVE OBJECTSoccurs is represented by an active object.
Typically the active object alsoinitiates its own asynchronous request, as was the case in this example.The decision as to which active object should be called upon toprocess an event is taken by the waiter, called the active scheduler. Theactive scheduler is responsible for listening to a signal that informs itwhen a request has been completed, and then it decides which activeobject is responsible for handling the associated event.Symbian OS provides a framework for asynchronous requests, activeobjects and an active scheduler at its lowest level.
Each thread cancontain only one current active scheduler, but the scheduler can containmany active objects. We look at this in more detail in the followingsections.Asynchronous requests are, more often than not, handled by servers.While it is useful to know this, it is not a prerequisite to understandingthe way in which active objects work.6.2 Multitasking and Pre-emptionSymbian OS implements pre-emptive multitasking so that it can runmultiple applications and servers concurrently. Active objects are usedto implement non-pre-emptive multitasking within the context of a singlethread.In Figure 6.2, we assumed that events, or request completions, occurin some kind of predictable order and that our thread gets a chance todeal with an event before the next event occurs.
Of course, in realityit doesn’t happen like that. The timing of events is unpredictable, andwhen our thread gets a chance to run, there may be more than oneevent ready to be handled. This is why active objects, like threads, havepriorities that affect their scheduling. On completion of an active object’sexecution, such as handling a timer event, control returns to the activescheduler, which then schedules further active object calls according tothe following rules:• if there is just one object eligible to run, run it• if there is more than one eligible object, choose the one with thehighest priority• if there are no eligible objects, then wait for the next event and decidewhat to do based on these rules.The function that gets called to handle an event is the active object’sRunL().