http://www.developer.com/ws/brew/article.php/3370971/C-Idioms-in-BREW-Part-2.htm
This installment is entirely dedicated to a central BREW design pattern; I've called it "Beware of the Watchdog." Maybe this is the most significant distinction between BREW and other mobile platforms (a notable exception being C++ Blackberry): for the sake of simplicity, BREW doesn't hide all the details of the operating system; developers are forced to take additional responsibility. After describing the problem, the article presents a C++ idiom designed to solve the problem in an automatic way. Problem: Context: Forces: Solution [1]: Example: Let's start with a very simple example, in pseudo-code: This is everyday programming, based on some well-established assumptions and expectations: But, look what happens when moving from our well-behaved platform to BREW. First of all, the initial code might fail miserably; the long loop triggers the watchdog and a reset is guaranteed. The next try might be something like this pseudo-code: Since BREW 2.1, is possible to use the IThread API to mimic blocking calls, but this is not always the easiest or the most convenient way—especially when all you want to do is to iterate through a container, for example! BREW way is asynchronous: Note: You always have to "yield control back to BREW." Basically, this means that you have to explicitly exit your code: Looks simple enough. Unfortunately, this is not the case: One C++ idiom, presented in the accompanying code, is to package the loops separately, resulting in a usage pattern like this: Because every task prepackages a loop, there are actually two types of tasks, corresponding to <for> and <while>. Of course, it makes sense to offer them as singletons [2], like this: A task is defined as: A parent class P is assumed, whose type can be deduced from TASK_TYPE. <task> is a singleton and as such is a member of P [2]. The class used for tests is a singleton too, but this is not required by the framework; the only requirement is to be capable of maintaining its internal test for <while> loops (and absolutely no requirements for <for>). TaskPolicy is actually a time policy and defines the maximum time to be spent in the loop: ClassToBeProcessed has to expose a completion member function, always of type: and a body member function: The int parameter in EXECUTION_FUN is the actual index of the loop, passed as a convenience back to the caller. <Task> encapsulates the BREW concepts: IShell::Resume() and the CALLBACK family of helper functions. <Task> is further specialized in <Task4> (masquerading <for> loops) and <TaskW> (<while>). The actual specialization is done in the callback method: This is a Test class that uses the above presented concepts: Resulting Context: Download the accompany code file here.
C++ Idioms in BREW, Part 2
June 21, 2004
Introduction
Beware of the Watchdog
while(whileBody ())
{}
doSomethingElse();
long time = getCurrentTime();
while(whileBody ())
{
if ((getCurrentTime()-time )> watchdog_cycle) yield();
}
doSomethingElse();
void fun()
long time = getCurrentTime();
while(whileBody ())
{
if ((getCurrentTime()-time )> watchdog_cycle)
{
registerCallback(fun);
return;
}
}
doSomethingElse();
}
//get the executor from a factory
TASK* t = getTaskInstance<UserClass>(& UserClass:: whileBody,
& UserClass:: doSomethingElse);
//sanity check
if (!t) return;
//set parameters
t->setLoop(); // t->setLoop(stop, start, step);
//run the show
t->run();
template <class C >
static TASK_TYPE* getTaskInstance(TASK_TYPE::EXECUTION_FUN tf,
TASK_TYPE::COMPLETION_FUN cf)
{
typedef TASK_TYPE::PARENT_TYPE P;
P * c = getInstance< P >();
if (c)
{
if (c->task_==0)
c->task_ = new TASK_TYPE (getInstance<C>(), tf, cf);
return c->task_;
}
return 0;
}
typedef Task<ParentClass, ClassToBeProcessed, TaskPolicy> TASK_TYPE;
struct TaskPolicy
{
static long getDurationLimit()
{
return 500; //millisecs -
}
};
typedef void (ClassToBeProcessed::*COMPLETION_FUN)();
// for loops
typedef void (ClassToBeProcessed::*EXECUTION_FUN)(int);
// while loops
typedef bool (ClassToBeProcessed::*EXECUTION_FUN)(int);
Anatomy of a Task
template <class M>
struct Task
{
typedef M PARENT_TYPE;
void run()
{
ISHELL_Resume (M::getInstance<M>()->m_pIShell, &cbk_);
}
protected:
Task()
{}
~Task()
{
CALLBACK_Cancel(&cbk_);
}
void init(PFNNOTIFY pfn)
{
CALLBACK_Cancel(&cbk_);
CALLBACK_Init(&cbk_, pfn, this);
cbk_.pfnCancel = 0;
}
private:
AEECallback cbk_;
private:
Task(const Task&);
Task& operator=(const Task&);
};
template <class M, class T, class P>
struct Task4 : public Task<M>
{
typedef void (T::*COMPLETION_FUN)();
typedef void (T::*EXECUTION_FUN)(int);
Task4(T* t, EXECUTION_FUN pfn, COMPLETION_FUN cfn): t_(t),
pfn_(pfn), cfn_(cfn),
start_(0), stop_(0), step_(0), duration_(0)
{ }
void setLoop(int stop, int start=0, int step=1)
{
start_ = start;
stop_ = stop;
step_ = step;
init((PFNNOTIFY)execTskImpl);
}
private:
static void execTskImpl(Task4* t)
{
T* a = t->t_;
t->duration_ = GETUPTIMEMS();
for (int i = t->start_; i < t->stop_; i+=t->step_)
{
(*a.*(t->pfn_))(i);
long duration = GETUPTIMEMS() - t->duration_;
if (duration>P::getDurationLimit())
{
t->run();
t->start_=i;
return;
}
}
t->start_ = t->start_;
(*a.*(t->cfn_))();
}
private:
int start_;
int stop_;
int step_;
long duration_;
T* t_;
EXECUTION_FUN pfn_;
COMPLETION_FUN cfn_;
private:
Task4(const Task4&);
Task4& operator=(const Task4&);
};
template <class M, class T, class P>
struct TaskW : public Task<M>
{
typedef void (T::*COMPLETION_FUN)();
typedef bool (T::*EXECUTION_FUN)(int);
TaskW(T* t, EXECUTION_FUN pfn, COMPLETION_FUN cfn): t_(t),
pfn_(pfn), cfn_(cfn),
duration_(0)
{ }
void setLoop()
{
i_ = 0;
init((PFNNOTIFY)execTskImpl);
}
private:
static void execTskImpl(TaskW* t)
{
T* a = t->t_;
t->duration_ = GETUPTIMEMS();
while( (*a.*(t->pfn_))(t->i_++) )
{
long duration = GETUPTIMEMS() - t->duration_;
if (duration>P::getDurationLimit())
{
t->run();
return;
}
}
(*a.*(t->cfn_))();
}
private:
long duration_;
T* t_;
int i_;
EXECUTION_FUN pfn_;
COMPLETION_FUN cfn_;
private:
TaskW(const TaskW&);
TaskW& operator=(const TaskW&);
};
static void execTskImpl();
struct Test
{
Test() : stop_(1000)
{}
void forBody(int idx)
{
Writer writer;
char ch[12];
SPRINTF(ch, "%ld", idx);
writer.DisplayOutput(2,ch);
}
void completionRoutine()
{
Writer writer;
writer.WriteLine("DONE");
}
bool whileBody(int idx)
{
Writer writer;
char ch[12];
SPRINTF(ch, "%ld", idx);
writer.DisplayOutput(3,ch);
return !(idx == stop_);
}
private:
int stop_;
};
The Code
References