Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1719490
  • 博文数量: 177
  • 博客积分: 9416
  • 博客等级: 中将
  • 技术积分: 2513
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-06 16:08
文章分类

全部博文(177)

文章存档

2013年(4)

2012年(13)

2011年(9)

2010年(71)

2009年(12)

2008年(11)

2007年(32)

2006年(25)

分类: C/C++

2009-02-26 12:47:52

Introduction

Symbian OS depends heavily on asynchronous services and operations. This is required by the nature of Symbian OS: micro-core and C/S architecture, quick response and running on handsets. There are solutions for asynchronous operation such as multi-threading. Multi-threading is a good solution on desktops and workstations. However, on handsets, it’s inappropriate to create so many threads due to limited system resource. User will get performance penalty on handsets using multi-threading solution, especially when running many applications.

To make a tradeoff, Symbian introduced the paradigm Active Object to serve as an alternative. In general, AO is a paradigm for non-preempt, multi-tasking in a single thread. By using AO, the number of threads can be reduced significantly because few threads (typically 2, 1 for server and 1 for client.) are needed to perform request/response operations. AO is replacement for multi-thread that sends request and receive response. One can use 1 AO for each kind of asynchronous request. For example, in 1 thread, one can create 3 AO’s: 1 to wait for user input, 1 to read file, the 3rd to communicate with other devices through socket. So far we will only consider the client thread. Same applies to server thread. As for AO and C/S internals, I will not talk about details because: I myself am not very familiar with the internals and, it will be another big story which cannot be elaborated in 1 essay.

Overview


Consider the figure shown above, How AO and AS work is quite clear and simple. First we create an AS and install it to a thread. Then in that thread, we create some AO’s and add them to the task queue of the AS. After issue first request to service provider, we start the event loop of AS. This will cause a P() operation on request semaphore of the thread. If the request is done, service provider will recognize the requesting thread (another topic) then do a V() operation on request semaphore on that thread and set the status of corresponding AO to complete. The AS will be waken up upon the event. It will traverse the task queue to find the AO which in complete status, and then invoke the RunL() of the AO in a TRAP. (I’m sure this paragraph contains so much information that you are now lost. Don’t worry, because we don’t need to know these so far;-)

Lifecycle of AO and AS

The figure shown above is not accurate but it demonstrates the typical use of AO and AS. The server itself may be implemented using AO’s

Example

The simplest one is to read a char from input and write to the screen through console. No comments are needed:


class CActiveConsole : public CActive
   {
public:
    static CActiveConsole* NewL(CConsoleBase*);
    ~CActiveConsole();
protected:
    CActiveConsole(CConsoleBase*);
    void ConstructL();
    void DoCancel();
    void RunL();
    void IssueRequest();
private:
    CConsoleBase* iConsole;
    };
CActiveConsole::CActiveConsole(CConsoleBase* aConsole) :
    CActive(CActive::EPriorityUserInput), iConsole(aConsole)
    {
    }
CActiveConsole::~CActiveConsole()
    {
    Cancel();
    }
CActiveConsole* CActiveConsole::NewL(CConsoleBase* aConsole)
    {
    CActiveConsole* self = new (ELeave) CActiveConsole(aConsole);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }
void CActiveConsole::ConstructL()
    {
    CActiveScheduler::Add(this);
    IssueRequest();
    }
void CActiveConsole::IssueRequest()
    {
    iConsole->Read(iStatus);
    SetActive();
    }
void CActiveConsole::RunL()
    {
    TChar ch = TChar(iConsole->KeyCode());
    if (ch == EKeyEscape)
        {
        CActiveScheduler::Stop();
        }
    else
        {
        _LIT(KFormat2,"You input: %c\n");
        _LIT(KFormat3,"You input: %d\n");
        if (ch.IsAlphaDigit()|| ch.IsSpace())

            iConsole->Printf(KFormat2, TUint(ch));
        else
            iConsole->Printf(KFormat3, TUint(ch));
        IssueRequest();
        }
    }
void CActiveConsole::DoCancel()
    {
    iConsole->ReadCancel();
    }
// Local Functions
LOCAL_C void MainL()
    {
    //
    // add your program code here, example code below
    //
    console->Write(_L("Press ESC to exit.\nEcho charactor you input\n"));
    CActiveConsole* ac = CActiveConsole::NewL(console);
    CActiveScheduler::Start();
    delete ac;
    }
LOCAL_C void DoStartL()
    {
    // Create active scheduler (to run active objects)
    CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
    CleanupStack::PushL(scheduler);
    CActiveScheduler::Install(scheduler);
    MainL();
    // Delete active scheduler
    CleanupStack::PopAndDestroy(scheduler);
    }

Below is modified example from developer’s library. However, it’s not good practice of software engineering. The original one can be found in SDK.

class CTimedMessenger : public CTimer
    {
public:
    CTimedMessenger(const TDesC&, CConsoleBase*);
    ~CTimedMessenger();
public:
    static CTimedMessenger* NewL(const TDesC&, CConsoleBase*);
public:
    void ConstructL();
    void IssueRequest();
    void DoCancel();
    void RunL();
public:
    TBufC<20> iGreeting; // Text of the greeting.
    CConsoleBase* iConsole;
    };
CTimedMessenger::CTimedMessenger(const TDesC& aGreeting, CConsoleBase* aConsole) :
    CTimer(CActive::EPriorityStandard), iGreeting(aGreeting),
            iConsole(aConsole)
    {
    }
CTimedMessenger* CTimedMessenger::NewL(const TDesC& aGreeting,
        CConsoleBase* aConsole)
    {
    CTimedMessenger* self = new (ELeave) CTimedMessenger(aGreeting, aConsole);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }
void CTimedMessenger::ConstructL()
    {
    CTimer::ConstructL();
    CActiveScheduler::Add(this);
    }
CTimedMessenger::~CTimedMessenger()
    {
    Cancel();
    }
void CTimedMessenger::DoCancel()
    {
    CTimer::DoCancel();
    iConsole->Write(_L("Outstanding Messenger request cancelled\n"));
    }
void CTimedMessenger::IssueRequest()
    {
    if (!IsActive())
        CTimer::After(1000000); // Calls SetActive() implicitly
    else
        iConsole->Write(_L("Already active\n"));
    }
void CTimedMessenger::RunL()
    {
    console->Write(iGreeting);
    IssueRequest();
    }
class CActiveConsoleMessenger : public CActive
    {
public:
    static CActiveConsoleMessenger* NewL(CConsoleBase*);
    ~CActiveConsoleMessenger();
protected:
    CActiveConsoleMessenger(CConsoleBase*);
    void ConstructL();
    void DoCancel();
    void RunL();
    void IssueRequest();
private:
    CConsoleBase* iConsole;
    CTimedMessenger* iMessenger;
    };
CActiveConsoleMessenger::CActiveConsoleMessenger(CConsoleBase* aConsole) :
    CActive(CActive::EPriorityUserInput), iConsole(aConsole), iMessenger(NULL)
    {
    }
CActiveConsoleMessenger::~CActiveConsoleMessenger()
    {
    Cancel();
    delete iMessenger;
    }
CActiveConsoleMessenger* CActiveConsoleMessenger::NewL(CConsoleBase* aConsole)
    {
    CActiveConsoleMessenger* self = new (ELeave) CActiveConsoleMessenger(aConsole);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }
void CActiveConsoleMessenger::ConstructL()
    {
    iMessenger = CTimedMessenger::NewL(_L("Good day!\n"), iConsole);
    CActiveScheduler::Add(this);
    IssueRequest();
    }
void CActiveConsoleMessenger::IssueRequest()
    {
    iConsole->Read(iStatus);
    SetActive();
    }
void CActiveConsoleMessenger::RunL()
    {
    TChar ch = TChar(iConsole->KeyCode());
    if (ch == EKeyEscape)
        {
        iMessenger->Cancel();
        CActiveScheduler::Stop();
        }
    else
        {
        if (ch == 'm')
            {
            iConsole->Write(_L("Starting messenger.\n"));
            iMessenger->IssueRequest();
            }
        else if (ch == 'c')
            iMessenger->Cancel();
        IssueRequest();
        }
    }
void CActiveConsoleMessenger::DoCancel()
    {
    iConsole->ReadCancel();
    }
LOCAL_C void MainL()
    {
    //
    // add your program code here, example code below
    //
    console->Write(_L("Press ESC to exit.\n'm' to start, 'c' to cancel.\n\n"));
    CActiveConsoleMessenger* acm = CActiveConsoleMessenger::NewL(console);
    CActiveScheduler::Start();
    delete acm;
    }

Pitfalls

Obviously, non-preempt means tasks with higher priority may not be run until other task’s RunL() returns. The priority helps, but not ensures tasks with higher priority will be run first. The second pitfall is when RunL() is time consuming, it’s harmful to other AO’s which may not have opportunity to run. With careful design and programming, this pitfall can be minimized.


Copyleft (C) 2007-2009 raof01.
本文可以用于除商业外的所有用途。此处“用途”包括(但不限于)拷贝/翻译(部分或全部),不包括根据本文描述来产生代码及思想。若用于非商业,请保留此 权利声明,并标明文章原始地址和作者信息;若要用于商业,请与作者联系(raof01@gmail.com),否则作者将使用法律来保证权利。

阅读(1849) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~