Full size Banner

Asyncronuos function call in Symbian by using active objects

Before starting our topics let's take a look what is asyncronuos/syncronuos functions or services in general. Asyncronuos service means we get some notification from the system when the task is done. It is comparable to this scenareo, for example I tell you to do something and I continue doing my own work. When you have done your work then you notify me via some media that your work has been done. I ask the result of your work and I use it. On the other hand the synconuos services means I tell you to do some thing and I just wait for you to finish the task, I don't move or do anything else than just waiting for you to finish the task. When you have done the task and you give the result then I can use it and after that I can continue with the works.
In software we have similar ideas, different modules works toghether to realise some use cases.
Asyncronuos Call
A client thread calls a method, which constructs a message and sends it to the server thread. When the message is sent, the client's thread returns from the method. The server side has its own thread that gets messages. It reads the passed data and processes the request. When the server has completed the request, it notifies the client's thread that the request has been completed, and passes a completion code as a result. The client thread must process the notification and result code, but it can be done at a suitable time.
Syncronuos Call
Syncronuos method is similar to the asynchronous method, but the client method doesnt return after message sending to server. Instead, it suspends the thread execution and waits for notification from the server about request completion. When the server sends the response to the client side, Symbian OS wakes up the suspended client thread. Then the client thread processes the completion code, handles errors if the completion code indicates an error in the server side, and returns.
When a synchronous method is called, the client thread is blocked until the server thread notifies it that the request has been completed. In many cases the client program must update its UI and process the user input events. If the synchronous method waits a long time to complete, the user experience suffers because the events are not processed. When the program utilizes asynchronous methods, the client thread can process the events and update the UI while the server performs the request. When the server has processed the request, the client is notified about the completion. The client processes the notification in the same manner as other events. In this case the user experience will be good, because the program is responding to the user's actions, and thus seems to work smoothly. It is easier to use synchronous methods than asynchronous methods. When an asynchronous method is used, the caller must remember each asynchronous method call and track if one has completed. When the synchronous method returns, the result can be processed immediately so there is no need to track the completion of requests. However, Symbian OS provides a framework that makes it easy to use asynchronous services: active objects and an active scheduler. Efficiency and a good user experience are among the reasons that asynchronous, rather than synchronous, interfaces should be used.
Symbian OS implements preemptive multithreading so that it can run multiple applications and servers simultaneously.

Clarification of some related terms will be helpful.
Multitasking
Multitasking is a technique used in an operating system for sharing a single processor between several independent jobs.
Under cooperative multitasking, the running task decides when to give up the CPU,
and under preemptive multitasking, a system process called the "scheduler" suspends the currently running task after it has run for a fixed period known as a "time slice." In both cases, the scheduler is responsible for selecting the next task to run and (re)starting it.
Multithreading means sharing a single CPU between multiple tasks (or "threads") in a way designed to minimize the time required to switch threads. This is accomplished by sharing as much as possible of the program execution environment between the different threads so that very little state needs to be saved and restored when changing threads.

Symbian OS implements preemptive multithreading.
A thread can do work independently. When we create an application in Symbian, we actually create an Process with the operating system. The process may contain many threads including the main thread. Main thread is created by the system when the process is created. Additional thread can be created base on our requirements for the application.
In Symbian OS, if we want to handle asyncronuos service then the thread (which one is going to handle the asyncronuos service) must have Active Scheduler and Active Objects. For each asyncronuos request we tell the active object (a class derived from CActive) that we are interested to get notification when the service provider (another module in the system) has finished the task. Active Scheduler get notified (via kernel) and RunL() method of the active object is called.
The relation between Process, thread, Active Scheduler and Active Objects are shown in the following diagram.


It is clear that each process (application) must have at least 1 thread. If the thread want to handle asyncronuos service then it should have at most active scheduler (AS). One active scheduler can have zero or more active objects. If the thread does not handle any asyncronuos service then it is not necessary to have it for the thread.
Each thread can have mutiple Active objects with one Active Scheduler(AS). Following diagram shows that each thread gets time slice from system scheduler.


Active Scheduler
In a complex system such as mobile phone we may have many tasks running simultaneosly. For example we should get notification when some body is going to call us. We don't know when some body will call us. We should get timer event (some signal from clock that time has expired), we should know also when user is pressing a keys. All these events are monitored by different process in the system. All those process who are going to handle such these type of asyncronuos events they must have Active Scheduler installed.
Symbian OS provides a framework that makes it easy to use asynchronous services: active objects and an active scheduler.
Active Scheduler controls the handling of asynchronous requests as represented by active objects.
Efficiency and a good user experience are among the reasons that asynchronous, rather than synchronous, interfaces should be used.
If we create an own thread, there isnt an active scheduler by default for the thread. Executables (.exe) have a main thread, like applications, but the thread doesnt have any active scheduler installed. To use active objects in such a thread, we have to install the active scheduler, run it, and remove it. Symbian OS provides a class named CActiveScheduler which is derived from CBase. Following code shows main methods in the class.
class CActiveScheduler : public CBase
{
...
IMPORT_C static void Install(CActiveScheduler* aScheduler);
IMPORT_C static void Add(CActive* aActive);
IMPORT_C static void Start();
IMPORT_C static void Stop();
IMPORT_C virtual void WaitForAnyRequest();
IMPORT_C virtual void Error(TInt aError) const;
private:
TPriQue iActiveQ;
};
This class is declared in e32base.h in our SDK's include folder. From the private area of the class we see that it has a list of CActive objects. Let's talk some thing about these methods.
Install() method installed the active scheduler for the current thread. For GUI application, this is done by framework. Following code shows how to call this method.
// Construct and install the active scheduler
CActiveScheduler* exampleScheduler = new (ELeave) CActiveScheduler;
// Install as the active scheduler
CActiveScheduler::Install(exampleScheduler);
When we call add() method then the active object is added to the doubly linked list of CActive (iActiveQ in CActiveScheduler class ).
Start() method starts the scheduler so it can start monitoring the active objects. There are some methods which can be used to customise the CActiveScheduler that means we can derive a class from CActiveScheduler and override WaitForAnyRequest() and Error() methods to give different behaviour (since these are virtual methods).

Active Objects
Active objects provide an easy way to process events. When an asynchronous request is issued, the active object's internal attribute is passed to take the response code when the server completes the request. The active scheduler takes care that the active object is run to process the result when the request completes at server side. There are many reasons why active objects, rather than threads, should be used to process events in Symbian OS.
For example:
  • Communication between threads is slower and more difficult than between active objects (active objects run in the same thread so they dont need any special way to share data).
  • Context switching between threads eats CPU cycles and thus decreases performance. Active objects are not interrupted like threads, so the CPU and Memory Mapping Unit (MMU) state doesnt have to be stored and restored between the switch.
  • Many resources cant be shared between threads. Because active objects are run with one thread, they can share resources.
  • Accessing memory or other shared memory between threads usually needs protection from simultaneous updates. This is done with semaphores, which may lead to complex code and decreased performance. Active objects dont need to worry about synchronization problems because they are scheduled in a nonpreemptive manner.
class CActive : public CBase
{
enum TPriority
{
EPriorityIdle=-100,
...
EPriorityHigh=20,
};
inline TBool IsActive() const;
IMPORT_C void SetActive();
virtual void DoCancel() =0;
virtual void RunL() =0;
TRequestStatus iStatus;
};

As we see that CActive is derived from CBase class and it has couple of (DoCancel() and RunL()) pure virtual methods. That means if we want to use this class we have to derive a class from CActive and implement the pure virtual methods. There is another variable TRequestStatus that tells the status of the active object.
We can set the pririty of the active objects. We can check if the active object has a request outstanding (there has been request made previously) by IsActive();
If we have made a request to service provider and now we think we want to cancel it, this can be done by DoCancel() methods. In our implementation we should call the cancelling method provided by service provider.
Following code example shows how we implement the DoCancel() methods.
void CActiveTimer::DoCancel()
{
iTimer.Cancel();
}

SetActive() method indicates that the active object has issued a request and that it is now outstanding. Derived classes must call this function after issuing a request.
Following code example shows how we issue a request.
void CActiveTimer::After(TTimeIntervalMicroSeconds32 anInterval)
{
if(!IsActive())
{
iTimer.After(iStatus, anInterval);
SetActive();
}
}
We check if there is an outstanding request by IsActive() and if not we make a new request to our timer class (service provider in this case) by calling After() method. We are passing iStatus member variable that is declared in CActive class. TRequestStatus indicates the completion status of a request made to a service provider (other module who is going to do the work for our module). When a thread makes a request, it passes a request status as a parameter. On completion, the provider signals the requesting thread's request semaphore and stores a completion code in the request status. Typically, this is KErrNone or one of the other system-wide error codes.
How Active Objects and Active Scheduler work together

To use active objects in a thread that doesnt have an active scheduler, then we must:
1. Create and install an active scheduler for the thread with method CActiveScheduler::Install().
2. Create active objects and add them to the active scheduler with method CActiveScheduler::Add().
3. Make one or more asynchronous requests with active objects.
4. Start the active scheduler with method CActiveScheduler::Start().
The active scheduler's method start() doesnt return until the active scheduler's stop method is called. Thats why there must be active objects in the scheduler to process some tasks. When the tasks are processed, one active object must stop the active scheduler with method CActiveScheduler::Stop().
When we have issued a request then the iStatus member variable is set KRequestPending. When the service provider has finished the task then the active object's iStatus member variable is changed to something else than KRequestPending. From this active scheduler knows that service provide has completed the task and active object's RunL() is called. This is handled by active scheduler's code. Following code sample shows how it is done.
1: void CActiveScheduler::DoStart()
2: {
3: TDblQueIter q(iActiveQ);
4: TInt level=iLevel++;
5: while (iLevel>level)
6: {
7: WaitForAnyRequest();
8: q.SetToFirst();
9: FOREVER
10: {
11: CActive *pR=q++;
12: __ASSERT_ALWAYS(
13: pR != NULL, Panic(EReqStrayEvent) );
14: if ( pR->IsActive()
15: && pR->iStatus!=KRequestPending )
16: {
17: pR->iActive=EFalse;
18: TRAPD(r,pR->RunL());
19: if (r!=KErrNone)
20: {
21: r = pR->RunError(r);
22: if (r!=KErrNone)
23: Error(r);
24: }
25: break;
26: }
27: }
28: }
29: }

The method DoStart() is called when the active scheduler is started. There are a few important issues that this implementation raises:
The active scheduler runs until the stop method is called. The stop method decreases the level attribute, which ends the while loop at line 5. The active scheduler iterates over active objects in order of priority. The iterator is created in line 3 and iterated in the FOREVER loop at line 9. The active scheduler suspends the thread until the first asynchronous request completes at line 7. The active object's RunL() is called only if the active object is activated (line 14) and the active object's status is not KRequestPending (line 15). That is why the active object must activate itself after calling the asynchronous request. When the active objects are iterated, only one active object's RunL() is called. A break at line 25 ends the for-loop if the active object was run. Lines 18 24 handles the error. If RunL() leaves then we must handle the error by overriding RunError() method and return KErrNone. Other wise the default Error method will be called and it will panic our application.
Now we have some basic understanding how active object works. Now let's take a look in these couple of diagram to refresh it.
Following diagram shows the relation between active scheduler, active object and the service provider. Note that the service provider is in other side of process boundary.



We can down load the power point presentation from by this link and example code from this link . If you run this example in Carbide and see the result, we are very much sure we shall have good knowledge about asyncronuos event handiling in Symbian OS.
Following diagram shows the relation between different objects in our example code.


If you have any problem to run, to understand you could contact us and also we value your feedback about this site and that will help us to improve it further.

Download area

Download source code

Download presentation material