Yielding the Processor
Yielding the processor in an IThread is done using
ISHELL_Resume and
ITHREAD_GetResumeCBK:
static void Thread1Main( SThreadCtx *pMe )
{
AECHAR wsz[ 16 ];
while( 1 )
{
pMe->n++;
WSPRINTF( wsz, sizeof( wsz ), L"t1=%d", pMe->n );
IDISPLAY_DrawText( pMe->pid, AEE_FONT_NORMAL, wsz, -1, 0, 0, NULL,
IDF_ALIGN_CENTER | IDF_ALIGN_TOP);
IDISPLAY_Update (pMe->pid);
ISHELL_Resume( pMe->pis, ITHREAD_GetResumeCBK( pMe->pit ) );
ITHREAD_Suspend( pMe->pit );
}
}
What raises eyebrows about this code for experienced BREW developers is the fifth linean infinite loop, a definite no-no. Otherwise, it's quite straightforward, until the last two lines, which obtains the
AEECallback instance for this thread at this particular point in execution, and then suspends thread execution. Under the hood, this code sets up the system's notion of the thread context at this point in your function, so when
ISHELL_Resume resumes execution, it will do so at the current point in your function where you called
ITHREAD_Suspend. This is functionally equivalent to a call to the Qualcomm-provided
IThread_Yield, available in the BREW SDK's
src/ directory in the file
AEETU_Yield.c:
void IThread_Yield( IThread *me, IShell *pIShell )
{
AEECallback *pcb = ITHREAD_GetResumeCBK( me );
ISHELL_Resume( pIShell, pcb );
ITHREAD_Suspend( me );
}
Why did Qualcomm break out the thread callback from the resume operation? Not just to keep us typing two lines instead of one, but to provide access to the callback for any callback-consuming function. Consider the Qualcomm-provided
IThread_GetHostByName, which mimicks a blocking
gethostbyname call, familiar to desktop developers using the Berkeley sockets interface:
int IThread_GetHostByName( IThread *me, INetMgr *piNet,
AEEDNSResult *pRes, const char *pszHost)
{
AEECallback *pcb = ITHREAD_GetResumeCBK(me);
INETMGR_GetHostByName(piNet, pRes, pszHost, pcb);
ITHREAD_Suspend(me);
if (((AEEDNSResult *)0 != pRes) &&
(pRes->nResult > 0) && (pRes->nResult <= AEEDNSMAXADDRS))
{
return pRes->nResult;
}
else
{
return 0;
}
}
By wrapping the
INETMGR_GetHostByName interface within its own thread, you can simulate blocking behavior by using the thread's own
AEECallback as the callback the
InetMgr invokes upon completion. Once the
INetMgr invokes the callback, execution continues on the line after the
ITHREAD_Suspend invocation as if the pair of calls to
INetMgr and
IThread were a synchronous call. This trick should work anywhere an interface consumes an
AEECallback; see the thread utilities provided by Qualcomm in recent drops of the SDK.
Obtaining the Result of an IThread's Execution
When a thread exits, it can provide a return value to the thread originator. Because of the prohibition on global variables in early versions of BREW, you must also provide the integer variable to store the return value. Remember the call to ITHREAD_Join in my EVT_APP_START handler?
CALLBACK_Init( &pMe->cbThread2Done, (PFNNOTIFY)Thread2Done, (void *)pMe );
ITHREAD_Join( pMe->stc2.pit, &pMe->cbThread2Done, &pMe->nThread2Result );
This causes the IThread to execute the callback function indicated by
pMe->cbThread2Done when the thread terminates. Within this function, you can get the exit value (an integer) like this:
static void Thread2Done( CApp *pMe )
{
DBGPRINTF( pMe->nThread2Result == SUCCESS ?
"Thread2 exit SUCCESS" :
"Thread2 exit FAILED" );
}
You can use
ITHREAD_Join anytime you need a programmatic action on thread termination.
Give IThread a Second Chance!
For those new to BREW, it's important to realize that BREW has threadsthey're just cooperative. And if you're an old saw with BREW, consider using BREW's IThread to replace some of your callback chains. You won't be disappointed.