Scopira
20080306
|
The sidekick ("mini") framework provides developers an easy to use method of performing intensive work in a dedicated worker thread, with the intent of letting the GUI thread continue to maintain the user interface and any user interactions.
A sidekick-enabled must be broken up into the following componets:
You often implement this using one of two techniques:
Both techniques are presented below.
First, make your GUI class (usually the window or widget that will manage the running process visually) a decendant of scopira::core::sidekick_i. Implement the run() method of this interface. The run method should have the form:
void run(void);
In this method, you will perform the the work that is to be done in the sidekick thread. TODO As the run() method is executed concurently with the GUI thread, great care must be taken to not allow of variable access conflicts. In particular:
The GUI thread can monitor the sidekick method's progress.
The simplest case involves simply checking the scopira::core::is_sidekick_running() function during a user initiated event.
To continuous and automically check the status without the user's intervetion, setup a scopira::coreui::uitimer object. This can be used for progress bars, continous plots, text out, etc, for example.
The GUI thread and sidekick thread may want to share information during the course of algorithm execution. This is particularly useful for sending intermidate and progress reports to the GUI thread, which can then report back to the user. The user may, via the GUI thread, decide to adjust the running parameters or cancel the job outright.
Any and all shared variables between the GUI and sidekick threads must be protected with a thread scopira::tool::mutex class. This is best done using a scopira::tool::shared_area concept, that encapsulates the data to be shared between two threads and protects and controls access via a mutex:
struct progress_area { int workdone, totalworktodo; }; scopira::tool::shared_area<progress_area> dm_progressarea;
Then, any code segments (both for the GUI thread or run() method thread) should be done via scopira::tool::locker_ptr RIIA idiom object. For example:
... other code ... { scopira::tool::locker_ptr<progress_area> L(dm_progressarea); // can now access the shared variables, knowing that the other thread cannot access it at the same time return L->workdone / L->totalworktodo; } ... other code ...
You could forgo the shared_area and locker_ptr classes and just use member variables and one mutex class directly, this is not recommended and is more error-prone.
The algorithm must be wrapped in a class that is a decendant of scopira::core::sidekick_i
The GUI thread should then (often, as a result of user-interaction):
The GUI thread can monitor the sidekick method's progress.
The simplest case involves simply checking the scopira::core::is_sidekick_running() function during a user initiated event.
To continuous and automically check the status without the user's intervetion, setup a scopira::coreui::uitimer object. This can be used for progress bars, continous plots, text out, etc, for example.
The GUI thread can also call methods within the running sidekick task to get more specific information about it's progress. However, as these inspection calls are done in the GUI thread, they may conflict with the sidekick thread who may be updating the same information that is being inspected.
To prevent this from happening, the common area should be in a scopira::tool::mutex-protected area. This can be done with the scopira::tool::shared_area
For example:
struct progress_area { int workdone, totalworktodo; }; scopira::tool::shared_area<progress_area> dm_progressarea;
Then, any code segments should be done via scopira::tool::locker_ptr RIIA idiom object. Your access method (which the GUI thread should call), could look like:
double ui_get_progress(void) { scopira::tool::locker_ptr<progress_area> L(dm_progressarea); return L->workdone / L->totalworktodo; }
Your actual worker code would like this (this would be in the run() method):
... algorithm code ... { scopira::tool::locker_ptr<progress_area> L(dm_progressarea); L->workdone += 1; // did one more task } ... algorithm code ...
You could forgo the shared_area and locker_ptr classes and just use member variables and one mutex class directly, this is not recommended and is more error-prone.