RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Cooperative Multithreading in BREW with IThread : Page 2

Learn how to harness cooperative multithreading in BREW to avoid writing applications that block the CPU during lengthy operations.

Starting a New IThread This article's application is a simple "hello world" application, thread-style (see Figure 1). It increments two counters in two threads, and just for fun, one thread also draws circles of incrementally larger size on the display. To show how threads can return values to the owning program, one thread exits after a set number of iterations; the other does not.

Figure 1. Hello World: A simple, thread-style application.

An IThread is an interface, just like any other, so you can make one simply by calling ISHELL_CreateInstance. Before you do, however, you should have someplace with appropriate scope to contain the reference to the thread, as well as a structure to contain any thread-local storage that the thread would want to share between the functions it invokes:

typedef struct _SThreadCtx
  IThread  *pit;
  int n;
  IDisplay *pid;
  IShell *pis;
  IGraphics *pig;
} SThreadCtx;

typedef struct _CApp
  AEEApplet a;
  SThreadCtx stc1, stc2;
  AEECallback cbThread2Done;
  int nThread2Result;
} CApp;
This is pretty simple stuff here. The application's threads will use an integer, access to the display, shell, and an IGraphics interface; all of this is wrapped in an SThreadCtx. The application itself will have two threads; for now, don't concern yourself with the purpose of the cbThread2Done or nThread2Result members of the CApp structure.

With the heap use out of the way, you can create and initialize your threads when your application begins. In the application's event handler, write:

    case EVT_APP_START:

      int r;
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_THREAD, 
                                 (void **)&pMe->stc1.pit );
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_THREAD, 
                                 (void **)&pMe->stc2.pit );
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_GRAPHICS, 
                                 (void **)&pMe->stc2.pig );
      pMe->stc1.pis = pMe->stc2.pis = p->m_pIShell;
      pMe->stc1.pid = pMe->stc2.pid = p->m_pIDisplay;

      CALLBACK_Init( &pMe->cbThread2Done, (PFNNOTIFY)Thread2Done, (void *)pMe );
      ITHREAD_HoldRsc( pMe->stc2.pit, (IBase *)pMe->stc2.pig );
      ITHREAD_Join( pMe->stc2.pit, &pMe->cbThread2Done, &pMe->nThread2Result );
      ITHREAD_Start( pMe->stc1.pit, 128, (PFNTHREAD)Thread1Start, 
                    (void *)&pMe->stc1);
      ITHREAD_Start( pMe->stc2.pit, 128, (PFNTHREAD)Thread2Start, 
                     (void *)&pMe->stc2);
      IDISPLAY_DrawText( p->m_pIDisplay, AEE_FONT_BOLD, szText, -1, 0, 0, NULL,
                         IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
      IDISPLAY_Update (p->m_pIDisplay);

This code:
  • Creates two IThread instances.
  • Creates the IGraphics instance required by the circle-drawing thread.
  • Joins the function Thread2Done with the second thread, so that it will be invoked when Thread2 exits.
  • Indicates that thread 2 will use the IGraphics interface.
  • Calls each thread's start function.
  • Draws "Hello World" to the display.
  • Updates the display.
  • The IThread calls are quite similar to POSIX thread calls. Qualcomm has also added the ITHREAD_HoldRsc and ITHREAD_ReleaseRsc interfaces to associate a BREW interface with a thread. The BREW ITHREAD_HoldRsc interface attaches a BREW interface to a thread. Oddly, it does not update the item's reference count, so don't make the mistake of releasing the held interface when you call ITHREAD_HoldRsc.

    For clarity, I've broken out the thread's main function and start function—you don't have to do this, but to me main on BREW suggests an event loop, whose first argument is the application context. By comparison, the PFNTHREAD function prototype for an IThread's main function has two arguments, the running thread and the thread context. Thus, my Thread1Start and Thread2Start functions look like this:

    static void Thread1Start( IThread *pThread, SThreadCtx *pMe )
      Thread1Main( pMe );
    static void Thread2Start( IThread *pThread, SThreadCtx *pMe )
      Thread2Main( pMe );
    This imposes a bit of space overhead in return for improved readability; in addition it provides a nice abstraction point in case I need to do additional setup work in the new thread before giving it something to do.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date