class ref name: unithread_o
category-group: os
layer: 3
header file: z_unithread.h

synopsis.
the unithread class object is a specialization for managing a single thread that continuously runs in the background. This object is particularly well-suited for GUI applications, such as those using Qt or (Microsoft) MFC. It was designed with 3 characteristics in mind:

  • the main flow of control (the "thread" started by the program entry point, such as main()) invokes a timer function (some function designated to be called periodically, such as once every 2 seconds);
  • the application has a big block of work to do that can be performed in [relatively] small increments;
  • there is only one instance of the unithread_o class.

description.
The unithread_o class is a "manager" class - there is one instance in the lifetime of the program. You need to know the characteristics and the purpose of this class in order to be able to correctly use it. Here are some facts about it:
Assign an integer code and a function for each background task. Suppose you have task A and task B, slated for background processing. You would make integer codes and functions like so:

  #define Code_bgtaskA 1
  #define Code_bgtaskB 2
  int taskA(int *pi) { /* app code */ return 0; }
  int taskB(int *pi) { /* app code */ return 0; }
  .
  void main()
  {
      unithread_o *pbgwork = launch_bgthread();
      pbgwork->add_task(Code_bgtaskA, taskA);
      pbgwork->add_task(Code_bgtaskB, taskB);
      pbgwork->set_task(Code_bgtaskA);          // starts task A
  }
The functions taskA() and taskB() should return 1 as long as there is more work to do. When a task is finished, it should return 0:
return code meaning
0 all done; set background work to "idle"
1 keep going. next call to the background task will re-enter the same function
-1 error occured. if "abort on error" is set, the background task will go to idle.
It can do only one task at a time. an internal variable, 'my_job', indicates what the current background job is currently in progress. A value of 0 means there is no current background task. Use the member function set_idle()() to set the current background job to nothing.

You add tasks with set_task(). This requires usage of a function pointer. Alternatively (to avoid function pointers), you can subclass from the unithread_o class and redefine the virtual function process_tasks(). In that case, your function will be called from process() with the current code value of the background task:

  #define Code_bgtaskA 1
  #define Code_bgtaskB 2
  int taskA(int *pi) { /* app code */ return 0; }
  int taskB(int *pi) { /* app code */ return 0; }
  .
  class My_BGUT : public unithread_o
  {
  protected:            // can be public
    virtual int process_tasks (const int);
  };
  int My_BGUT::process_tasks (const int x)
  {
     switch (x)                         // your application code
     {
     case Code_bgtaskA: return taskA(); break;
     case Code_bgtaskB: return taskB(); break;
     }
     return -1;
  }
  .
  void main()
  {
      unithread_o *pbgwork = launch_bgthread();
      pbgwork->set_task(Code_bgtaskA);          // starts task A
  }
In this example, everything inside My_BGUT::process_tasks() is supplied by you (ie, the application). The value of the input parameter 'x' is whatever the current background task code is (eg, Code_bgtaskA or Code_bgtaskB). This function must not return the value -2. That is reserved for the unithread object to determine whether or not it should use its internal list of tasks (populated by repeated calls to add_task()).

member functions (primary)

launch_bgthread()
SIGNATURE: unithread_o *launch_bgthread (int *pi = NULL)
SYNOPSIS: instantiates the (singleton) unithread instance.
DESCRIPTION:
this function is the preferred way to create a unithread object. since the intention is to have 1 unithread object in a program, this function creates the singleton object, sets an internal global pointer, and returns a pointer to the new object. any subsequent invocations return a pointer to the same object.
RETURNS: a pointer to the object.
TRAITS: this function is not a member of the unithread_o class - it is a global function
 

unithread_o()
SIGNATURE: unithread_o ()
SYNOPSIS: instantiates a unithread instance.
 

unithread_o(unithread_o)
SIGNATURE: unithread_o (const unithread_o &rhs)
SYNOPSIS: this function exists in order to prevent making a copy of the object. Nothing will be copied from 'rhs'.
 

operator = (unithread_o)
SIGNATURE: unithread_o &operator = (const unithread_o &rhs)
SYNOPSIS: this function exists in order to prevent making a copy of the object. Nothing will be copied from 'rhs'.
 

destructor
SIGNATURE: ~unithread_o ()
SYNOPSIS:
'destroys' the object: all data fields are reset, by calling member function reset(). If a call to shut_down() was made earlier, the function thread_o::terminate() will be invoked.
TRAITS: this function is virtual (as usual).
 

block_ordered()
SIGNATURE: boolean block_ordered () const
SYNOPSIS:
informational: tells if the object was commanded (requested?) to block. such a command is done by invoking member function force_block(), which should only be done by the main thread.
RETURNS:
TRUE: a block command was issued;
FALSE: otherwise.
TRAITS: this function is inline
 

is_blocked()
SIGNATURE: boolean is_blocked () const
SYNOPSIS:
informational: tells if the object is currently blocked. This happens when ordered to block (done via the main thread, by calling block_ordered()), and the thread executes another loop in unithread_o::process() - that is, it exits out of the current processing chunk.
RETURNS:
TRUE: process is blocked
FALSE: process is not blocked
TRAITS: this function is inline
 

is_paused()
SIGNATURE: boolean is_paused () const
SYNOPSIS:
tells if the background thread is paused. Pausing does not change the current job. If a pause-sleep time is specified (via set_delay()), the background thread will pause the specified number of milliseconds (via thread_o::msleep()).
TRAITS: this function is inline
 

howmuch_msleep_requested()
SIGNATURE: int howmuch_msleep_requested () const
SYNOPSIS:
returns the delay-pause time, in milliseconds. This time is set by invoking the member function set_delay(), which will request a pause delay.
RETURNS: the value of 'wantto_sleep' (if set) multiplied by 1000, or 'wantto_msleep' (as-is).
 

last_result()
SIGNATURE: int last_result (int *pi = NULL) const
SYNOPSIS:
returns the return value of the last background task invocation performed. If there is no current background job, 0 is returned. Optionally the error indicator parameter is set to whatever the current background job has set it to, unless there is no job at the moment, in which case it is set to 0.
PARAMETERS

  • pi: an error indicator output 'flag' variable. The value of this variable is set by the application. If there is no current background task, it is set to 0.
    If the background task completed successfully, it should be set to 0.
  • RETURNS: same as the return value of the current background task, or 0 if there is no task.
     

    user_data()
    SIGNATURE: void *user_data (const int x, int *pi = NULL)
    SYNOPSIS:
    provides a hook for background processes to access any application data. The background process needs to supply its job code (the same value used when invoking add_task()).
    PARAMETERS

    • x: [input]: application-supplied value to identify the task. Cannot be (less than or equal to) 0.
    • pi: [output] an error indicator variable. values:
      0: successful call;
      zErr_Index_OutofBounds: 'x' is less than zero
      zErr_NotFound: 'x' is not a valid code (set in add_task()).
     

    add_task()
    SIGNATURE: int add_task (const int x, int (*bgfunc)(int *), void *pad = NULL, int *pi = NULL)
    SYNOPSIS:
    this is the primary method for adding a function that is to be invoked by the background thread. A unique identifier ('x') needs to given. This method can be called repeatedly, once for each background task. A function that returns an integer and takes a (int *) parameter needs to be provided. This function will be called whenever the curent background job is set to 'x' and the background thread is free to perform work.
    PARAMETERS

    • x: [input]: application-supplied value to identify the task. Cannot be (less than or equal to) 0.
    • bgfunc: a pointer to the function to perform the task.
    • pad: [input] an optional application data pointer. This is set to NULL by default, which means there is no such pointer for the task.
    • pi: [output] an error indicator variable. values:
      0: successful call;
      zErr_Index_OutofBounds: 'x' is less than zero
      zErr_NotFound: 'x' is not a valid code (as set in add_task()).
     

    remove_task()
    SIGNATURE: int remove_task (const int x, int *pi = NULL)
    PARAMETERS

    • x: [input]: application-supplied value to identify the task. Cannot be (less than or equal to) 0. The value must have been supplied by a prior call to add_task() in order for this call to complete successfully.
    • pi: [output] an error indicator variable. values:
      0: successful call;
      zErr_Index_OutofBounds: 'x' is less than zero
      zErr_NotFound: 'x' is not a valid code (provided in add_task()).
     

    set_delay()
    SIGNATURE: int set_delay (const int dx)
    SYNOPSIS: [WIP]
     

    shut_down()
    SIGNATURE: int shut_down()
    SYNOPSIS: [WIP]
    TRAITS: this function is inline
     

    reset()
    SIGNATURE: int reset ()
    SYNOPSIS: resets all internal data to their default values.
    RETURNS: 0 (always)
     

    set_task()
    SIGNATURE: int set_task (const int x, int *pi = NULL)
    SYNOPSIS: sets the current background task to 'x'. This value should have been specified in a prior call to add_task().
     

    force_block()
    SIGNATURE: int force_block (int *pi = NULL)
    SYNOPSIS:
    instructs the backbround process/thread to block. If the thread is currently performing a background task, the task will not be interrupted, but it will be blocked upon resumption of the next iteration of unithread_o::process().
     

    let_unblock()
    SIGNATURE: int let_unblock (int *pi = NULL)
    SYNOPSIS: allows the backbround process/thread to run unfettered. This funtion is the converse of force_block().
     

    main_check()
    SIGNATURE: int main_check (int *pi = NULL)
    SYNOPSIS:
    this function should be invoked frequently by the main line flow of control (which started the program; "main()" or equivalent). It will toggle back and forth between blocking the background thread and unblocking it. The toggle will happen only after a state change in the background thread. It will not pre-emptively interrupt any currently active background task.
    This function is intended to be called every few seconds. In a GUI/window-based environment, the more frequently it is called, the less likely the window system will "freeze up". A granularity of under 10 seconds is recommended.
     

    set_idle()
    SIGNATURE: int set_idle()
    SYNOPSIS:
    sets an internal (boolean) flag that puts unithread_o::process() in "pause" mode and makes is_paused() return TRUE.
    TRAITS: this function is inline
     

    clear_tasks()
    SIGNATURE: int clear_tasks()
    SYNOPSIS: this is a simple alias for set_idle().
    TRAITS: this function is inline
     

    sleep_request()
    SIGNATURE: int sleep_request (int n)
    SYNOPSIS:
    set a pause-delay time, to be done in the next iteration within unithread_o::process(). This will make the background thread sleep 'n' seconds.
    this member function should be called only from within the background thread (in the task callback application code).
     

    msleep_request()
    SIGNATURE: int msleep_request (int n)
    SYNOPSIS:
    set a pause-delay time, to be done in the next iteration within unithread_o::process(). This will make the background thread sleep 'n' milliseconds. This function is a sibling to sleep_request().
    this member function should be called only from within the background thread (in the task callback application code).
     

    config()
    SIGNATURE: int config ()
    SYNOPSIS:
    this function provides a way for the background thread to do (optional) configuration when the thread starts up.
    By default, this function (if not redefined in a subclass) does nothing and returns 1.
    RETURNS: 1
    TRAITS: this is a virtual, protected function
     

    cleanup()
    SIGNATURE: int cleanup()
    SYNOPSIS:
    this function provides a way for the background thread to do (optional) post-processing "cleanup" when the thread completes its main loop. By default, unithread_o::cleanup() does nothing.
    RETURNS: 0
    TRAITS: this is a virtual, protected function
     

    process()
    SIGNATURE: int process()
    SYNOPSIS:
    Although this member function can be re-implemented in a subclass, this is highly discouraged. if so done, the function should call unithread_o::process(). If it is not called, this class becomes useless.
    TRAITS: this is a virtual, protected function
     

    process_tasks()
    SIGNATURE: int process_tasks (const int x, int *pi)
    SYNOPSIS: This member function provides an alternate way to
    PARAMETERS

    • x: [input]: application-supplied value that identifies the current task. Should not be (less than or equal to) 0.
    • pi: [output] an error indicator variable. This parameter is under application control. By convention, a 0 value should mean a successful call, and a non-0 value should be used when an error occurs
    TRAITS: this is a virtual, protected function
     

    usage.
    The following usage instructions should be treated as boilerplate example snippets. MFC is presented as the framework:
    [1] create an object instance by calling launch_bgthread():

    static unithread_o *px = NULL;
    int bgwork_fetchwebpages (int *);
    int bgwork_proc_webpages (int *);
    void main()
    {
        px = launch_bgthread();
        px->set_delay(750);
        // ..
        px->set_task(1);
    }
    

    [2] add the background tasks to the unithread instance:

    void main()
    {
        // ...
        px->add_task(1, bgwork_fetchwebpages);
        px->add_task(2, bgwork_proc_webpages);
    

    [3] configure a timer-driven function to call member function main_check(). In this example, MFC "view" class 'MyView' is used to call member function OnTimer() every 2 seconds:

    class CsciamView : public CFormView
    {
        // [MFC "view"-related code]
        UINT my_timer;              // 'SetTimer()' return value
    };
    

    void MyView::OnInitialUpdate() { my_timer = SetTimer(ID_TIMER_SECONDS, 2000, NULL); }

    void MyView::OnTimer (UINT_PTR nIDEvent) { // [other timer-related code] px->main_check(); }

    note.
    the "callback function" (taskA() in the discussion above) cannot access thread_o::sleep() (or msleep()). This can be done by using the handle returned by launch_bgthread() and calling member function sleep_request():

      unithread_o *px;
      int taskA(int *);
      void main()
      {
          px = launch_bgthread();
          px->add_task(Code_bgtaskA, taskA);   // sets up task A
          px->set_task(Code_bgtaskA);          // starts task A
      }
      int taskA(int *pi)
      {
        /* app code */
        px->msleep_request(250);               // request millisec sleep
        return 0;
      }
    

    examples.
    this example is console-based. A loop containing a 1-second sleep is used to emulate a timer function. For MFC, set up OnTimer() (as shown in the usage section). For Qt, bind a function to Qt's timer system via connect():

        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(bgwork_fetchwebpages()));
        timer->start(1000);
    
    In this watered-down example, the main loop loops 10 times. On the 3rd time, the background task is set. During each loop, the main thread calls "unithread_o::main_check()" 4 times, and sleeps 4 times, for 1 second. The call to main_check() toggles the background task (work()) to "suspended" (if it's currently free) or "unsuspended" (if currently suspended). This putting the task on hold is where the GUI (eg MFC, Qt, X-Windows, or other similar systems) has a chance to regain control, doing things such as refreshing or resizing windows, or updating widgets.
        #include "z_unithread.h"
        static unithread_o *px = NULL;
        int work(int *);
    
        void main (int argc, char **argv)
        {
            int i, j, ie0, ie1;
            z_start();
    
            px = launch_bgthread();
            if (px == NULL) return;
            px->set_delay (750);
            px->add_task(1, work);
    
            for (i=0; i < 10; i++)
            {
                if (i == 2)
                    px->set_task(1);
    
                for (j=0; j < 4; j++)
                {
                    px->main_check();           // toggle on/off suspending bg task
                    z_sleep(1);                 // a "simulated timer operation"
                }
                std::cout << "\nMAIN: loop #" << i << std::endl;
            }
    
            z_finish();
        }
    
        int work (int *pie)
        {
            static count_t j = 0;
            while (j < 100)
            {
                 // [perform some work the "jth" time]
                j++;
                return 1;
            }
            return 0;
        }