Make your own free website on Tripod.com
Title:       Asynchronous general call of MFC member function
Author:      Author Vladimir Grigoriev 
Email:       grigoriev@arktika.ru , du_volon@fromru.com
Environment: VC++ 6.0-7.1, Win2000
Keywords:    Thread,  MFC
Level:       "Intermediate"
Description: An article on thread call writing optimization
Section      Miscellaneous
SubSection   General

 

Asynchronous general call of MFC member function

 

 

 

Introduction

About:
   How to generally eliminate hang in MFC apps, converting MFC calls to half -asynchronous,
writing 1 string of code, with no need of disturbing application code, like writing synchronizations and callbacks,

  With remaining served all usual MFC and application messaging  processing, in not changing existent logic way,
basing on the fact that thread logic are often of definite ty
pe,
and can be implemented separately.
Instead of repetition of thread writing in different each time and not systematic way,
-this article about

Principle:
Based on replacing message pump at calling method and thread waiting , usually hanging place, eliminating GUI froze.


Practical purposes:


Better reaction of application and problem methods error processing can be made much better,
as well thread code more easy written.


Execution scheme: Usual MFC call and with modifying it to auto threaded call

Sample Image - maximum width is 600 pixels
Background 

Usually happen problems:

History:
I found that any application hangs at waiting any time consuming resource,
like web, DataBase, system drive,
or just loading other much data.
Demos: As can be seen in MyIe, very good browser, that hangs when error page is loaded, or when some extended filter, when applied hangs application - it's all other windows.

   Usually that problem is solved by creating thread function, calling it, synchronizing it with main thread
while it continue their execution - usually with main purpose visual interface initialization.

   So design is following - main thread process GUI, as MFC self intended for work good if no wait there is, and
instead of giving processor ability to be in rest while system must shown, process time-consuming task in parallel thread. Application became "reactive" to user interaction and for GUI messages and drawing.


   You want "call this member" and wait here and process in other places , due to it is your usual code writing method, and better one, so why to
write additional global code, each time, do it different, instead of just call member with condition of wait, and do
it generally - not loosing time? In one string of code.

So I decided to write code to release second pattern.

Using the code


Types of use threading in application, usage scenarios:

 - "call this member and wait here", BUT proceed GUI messages that wait time,
 - "call this member and no wait" ,   BUT proceed GUI messages
 - 
"call this member and wait some time and continue", BUT proceed GUI messages
last of them usually used for detecting that error happen,
this pattern process error situation intelligently:
detecting  "too long time for that operation is get", but not total application is hang, but in that isolated call only, and that 
may be reported and processing continued.


I write this code for scenario " wait here, BUT proceed GUI messages",
where GUI messages - is typically all that MFC does after initialization, so all the MFC work,
meaning that if someone who need no proceed GUI messages while wait, can easily write thread code
self, due to MFC API for that task is usual and simple enough.


if you have MFC call:

BOOL Cauto_threadDlg::OnInitDialog()
{

    int a =5;

    //member and call it's method:
    
    m_else_class . method_1_arg( &a );

    //... else code

}

class some_else_class
{
    public:
    int method_1_arg(int* j);
};


call you can rewrite as:

declaring local variable, with argument: member function type, pointer to function and
pointer to base class for that called method which is passed to that function as argument, 
so variables within method will be accesses transparently with no need to edit all within called method,
(that may be already written)
- and as all needed arguments holder. It may be this pointer, or member of "some_else_class",
if there is need in many arguments.
Also return value type and timeout of wait at place of call.

In a constructor thread will be created and method called, while making possible to handle 
this call waiting (see patterns of use) , or not,  or commanding it to change execution through interface class.

  typedef void (some_else_class::*F_def)(int*);

  CAutoThread<some_else_class, F_def, & some_else_class::method_1_arg, int*, int>

  local_thread_call( & m_else_class,5000,&a);



Method type:
method must be with one argument - class holding all that need or "this",
but not VOID and 
returning any type except VOID!

Than:
Application continue immediately GUI processing, like message delivery and reaction is continue to act. 
Wait 5 second and continues at place of call- useful for large time detection as error and make possible to report it.
If call execution finishes before it - application consider that all is normal and go further from call place.

 

This is done by replacing windows message pump by new pump within that variable implementation,
but: automatically for main thread only! Like MFC does.

To make your GUI created in separate thread reacted to input and show changes,
it need to place in the code of called method call to


ProcessMessages(); 

periodiacally, when you want to show changes,

declared as:

extern void ProcessMessages(HWND hwnd);

hwnd - handle of window that wanted to be message processed for,
or 0 for current thread, where it called. 

That gets current messages and dispatches to control for which they are purposed.

Planned:
You can see how I planned to do it automatically in Planned section, below, and advice how to do it better,
due this is still some problem. MFC is not thread - safe, but better to be.


Need to remember that call variable created in a sample -is local,
it is need not to define member, so it is works like some SmartPointer,
after loosing scope variable destroy self and no any destruction is need.

I use it for not main classes not dirtying by many declarations and economy 
code writing, "only at needed by logics place", and also avoiding synchronizing 
definitions that otherwise can make code not good observable.

But possibility of  synchronization across total application is provided:

 

Usual Synchronization routines with that class:

   Main point was also in writing suitable code that encapsulates all typical thread code in 1 line ,
with parameters that define threaded call.
Making repetitive task of creating threads and some synchronizations not need in case that are usual for MFC:
eliminating hangs in calls, but also
making synchronizations more easy written in cases where it really need:
when data need be prepared before used: 
for that fourth argument in constructor provided:


CAutoThread(T* pT, int
time, argType arg, CEvent_Auto* pCEvent_Auto =0);


so change constructor string to:

local_thread_call(&m_else_class,5000,&a, m_pCEvent_Auto) ;


Get also defined member variable
CEvent_Auto* m_pCEvent_Auto for access to synchronization Event;

which return CEvent_Auto* pointer to object that is usual CEvent pointer, for use with
standard synchronization code like:
That pointer is allocated from heap and will not be destroyed until you call

CEvent_Auto::Release();


like this:


if(::WaitForSingleObject( *m_pCEvent_Auto,20*000 ) ==WAIT_TIMEOUT)
{

    //Error message -20 seconds - too much for one1 MegaByte file read.
    // Disk dead to be. CD opened, or programmed bad.
}

m_pCEvent_Auto->Release();

//....Do your have data prepared - depended processing.


Note: that timer used is not most precise,- threaded timer precision is used plus
waiting for message Dispatch time, so if you have more hanging calls, timer will reflect
that hang period.

So can be detected call with probable hang at other places of application.

 

Error processing:


Calling methods within thread can help much to better respond to error situation,
than hang that sometimes can happen if no exception generated and problem method called is hanged.

You have all access into  within thread providing your class as argument of the method that is threaded,
inform it by setting some its member so it can exit its execution in a thread, calling within ExitThread(),
or changing its execution to better way than asking problem device, that is to be generated  error,
or you can terminate thread externally:
Thread pointer can be than get by call: 

    CWinThread* GetThread() 

and terminated calling  

    TerminateThread( CWinThread);

note than this call is considered in all documentation as not safe for stack clearance due to their forcing character,
but useful in serious situation.

 

Usual checking result procedure can be written as folows:

int iRetVal;

if(local_thread_call.GetStatus() != CALL_Finished)
{
    int retval =
    
    ::MessageBox(0,"TimeOut over but result not received :! \n    \
                                                                                                        \
                        Will you wait or terminate ?"," ERROR", MB_OK | MB_OKCANCEL);

    if(ID_OK ==retval)
    {
           TerminateThread( *GetThread() );
    }
}
else
{

    iRetVal = local_thread_call.GetRetVal();
}

Class Definition:


Call is defined as:

Locally created variable - template instantiation,
that creates working thread, in which desired member function is called.

template <class T,class typeOf_Fn, typeOf_Fn f,class argType, class RetValType >

class CAutoThread

constructor:

CAutoThread(T* pT, int time, argType arg, CEvent_Auto* pCEvent_Auto =0, DWORD dwCreateFlags =0)


class T                 - owing class called member function ("this" class)
class typeOf_Fn    - type of member function, or it's signature
typeOf_Fn f          - pointer to member function,
class argType       - type of class that will be passed as single argument, holding all others in need.
class RetValType  - type o return value.

argType arg - instance of argument class, usually better to use only pointer, to avoid unneeded coping,
that also can be error prone.

T* pT                  - instance of owing clas of called method.
int time               - timeout to wait method execution at place of call.
argType arg         - argument that must be passsed to method.



CEvent_Auto* pCEvent_Auto - returned pointer to syncronization CEvent.
 DWORD dwCreateFlags -  can be 0 to run immediately or
CREATE_SUSPENDED to create thread suspended and than Run, at needed time bu calling
than
CAutoThread::RunThread()  :
to make it possible to define as member
variable of AutoThread in a some some holding class, or not run execution of method by else reason, but design goal not propose reasonable possibility for it,
due to not public CWinThread* variable is planned to use - in this case better to define thread by self than to specialize all.



Usage:

BOOL Cauto_threadDlg::OnInitDialog()
{
   int a =5;
    typedef void (some_else_class::*F_def)(int*);

    CAutoThread<some_else_class, F_def, & some_else_class::method_1_arg, int, 5>
    local_thread_call (&m_else_class, 3000, a) ;
}
class Cauto_threadDlg
{
//...
    some_else_class   m_else_class;

};
class some_else_class
{
public:

  void method_1_arg(int* j)
  {
    AfxMessageBox("Threaded Member Called with 1 arg");


  CreateGUI(); //see more in detail at sample

    for(int i=0;i< *j; i++)
    {
        //Waiting Call
        ::Sleep(1000);

        // make GUI reacting:

        ProceesMessages();
   }
 
   return ;

};


Points of Interest

Planned:
Still is need to call this function periodically within thread planned to be made better reactive, but I plan to implement automatic thread GIU redrow assistance,
for the sake this is done in a displacingpreemptive way( win32), instead of cooperative, when you need to care about this self as proposed now. To place mesage processing to separate thread insted of "all in one" as in MFC currently, like in win16.