multithreading problem with overloading

I have a funny but frustrating problem with multithreading in overloaded classes. Please refer to the following code:

#include "stdafx.h"
#include <windows.h>
#include <conio.h>
#include <atlstr.h>

//
// base
//
class CThreadBase
{
public:
  CThreadBase();
  virtual ~CThreadBase();

      static unsigned __stdcall Execute(void* pParam);
  long Execute();

  virtual void OnBegin();
  virtual void OnEnd();

  HANDLE hThread;
  HANDLE hEvThreadTerminated;
  HANDLE hEvThreadTerminate;
};

CThreadBase::CThreadBase()
{
  hEvThreadTerminate = CreateEvent(NULL, FALSE, FALSE, NULL);
  hEvThreadTerminated = CreateEvent(NULL, FALSE, FALSE, NULL);
  unsigned int ThreadID;
  hThread = (HANDLE)_beginthreadex(NULL, 0, Execute, this, 0, &ThreadID);
}

CThreadBase::~CThreadBase()
{
  SetEvent(hEvThreadTerminate);
  WaitForSingleObject(hEvThreadTerminated, INFINITE);
  CloseHandle(hThread);
  CloseHandle(hEvThreadTerminate);
  CloseHandle(hEvThreadTerminated);
}

unsigned __stdcall CThreadBase::Execute(void* pParam)
{
  CThreadBase* Instance = (CThreadBase*)pParam;
  if ( Instance ) return Instance->Execute();
  return 0;
}

long CThreadBase::Execute()
{
  OnBegin();
  WaitForMultipleObjects(1, &hEvThreadTerminate, FALSE, INFINITE);
  OnEnd();
  SetEvent(hEvThreadTerminated);
  return 0;
}

void CThreadBase::OnBegin()
{
  printf("CThreadBase::OnBegin()\n");
}

void CThreadBase::OnEnd()
{
  printf("CThreadBase::OnEnd()\n");
}

//
// descendant
//
class CThreadDesc : public CThreadBase
{
public:
  virtual void OnBegin();
  virtual void OnEnd();
};

void CThreadDesc::OnBegin()
{
  printf("CThreadDesc::OnBegin()\n");
}

void CThreadDesc::OnEnd()
{
  printf("CThreadDesc::OnEnd()\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
  CThreadDesc* desc = new CThreadDesc();

  printf("\nAny key to stop the thread\n");
  getch();

  delete desc;

  printf("\nAny key...\n");
  getch();
  return 0;
}


I would expect and would like to see as output:

Any key to stop the thread
CThreadDesc::OnBegin()
CThreadDesc::OnEnd()

But I find:

Any key to stop the thread
CThreadDesc::OnBegin()
CThreadBase::OnEnd()

Somehow class information is lost? Any answer would be greatly appreciated.
thx, Jeroen.

jeroenrinsemaAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
AlexFMConnect With a Mentor Commented:
I think my answer is OK.
0
 
AlexFMCommented:
This is really interesting case. I think this happens because OnEnd() function is called from destructor, when CThreadDesc instance is already destroyed, this is why it calles CThreadBase function, it still exists. Stopping thread in other function works as expected. I post corrected code, with one more fix: hEvThreadTerminated is not necessary, it is possible to wait for thread handle intstead. You can add some code which ensures that thread is stopped in the destructor, if Stop function was not called before.

#include "stdafx.h"

#include <windows.h>
#include <conio.h>
#include <atlstr.h>

class CThreadBase
{
public:
    CThreadBase();
    virtual ~CThreadBase();

    static unsigned __stdcall Execute(void* pParam);
    long Execute();
    void Stop();

    virtual void OnBegin();
    virtual void OnEnd();

    HANDLE hThread;
    //HANDLE hEvThreadTerminated;
    HANDLE hEvThreadTerminate;
};

CThreadBase::CThreadBase()
{
    hEvThreadTerminate = CreateEvent(NULL, FALSE, FALSE, NULL);
//    hEvThreadTerminated = CreateEvent(NULL, FALSE, FALSE, NULL);
    unsigned int ThreadID;
    hThread = (HANDLE)_beginthreadex(NULL, 0, Execute, this, 0, &ThreadID);
}

CThreadBase::~CThreadBase()
{
//    SetEvent(hEvThreadTerminate);
//    WaitForSingleObject(hEvThreadTerminated, INFINITE);
    CloseHandle(hThread);
    CloseHandle(hEvThreadTerminate);
//    CloseHandle(hEvThreadTerminated);
}

void CThreadBase::Stop()
{
    SetEvent(hEvThreadTerminate);
//    WaitForSingleObject(hEvThreadTerminated, INFINITE);
    WaitForSingleObject(hThread, INFINITE);
//    CloseHandle(hThread);
//    CloseHandle(hEvThreadTerminate);
//    CloseHandle(hEvThreadTerminated);
}

unsigned __stdcall CThreadBase::Execute(void* pParam)
{
    CThreadBase* Instance = (CThreadBase*)pParam;
    if ( Instance ) return Instance->Execute();
    return 0;
}

long CThreadBase::Execute()
{
    OnBegin();
    WaitForMultipleObjects(1, &hEvThreadTerminate, FALSE, INFINITE);
    OnEnd();
//    SetEvent(hEvThreadTerminated);
    return 0;
}

void CThreadBase::OnBegin()
{
    printf("CThreadBase::OnBegin()\n");
}

void CThreadBase::OnEnd()
{
    printf("CThreadBase::OnEnd()\n");
}

//
// descendant
//
class CThreadDesc : public CThreadBase
{
public:
    CThreadDesc() : CThreadBase()
    {

    }
    virtual void OnBegin();
    virtual void OnEnd();
};

void CThreadDesc::OnBegin()
{
    printf("CThreadDesc::OnBegin()\n");
}

void CThreadDesc::OnEnd()
{
    printf("CThreadDesc::OnEnd()\n");
}



int _tmain(int argc, _TCHAR* argv[])
{
    CThreadDesc* desc = new CThreadDesc();

    printf("\nAny key to stop the thread\n");
    getch();

    desc->Stop();
    delete desc;

    printf("\nAny key...\n");
    getch();

    return 0;
}

0
 
jeroenrinsemaAuthor Commented:
It has to do with setting the Terminate event from within the destructor. The OnEnd() function is called when the class is already under destruction. The virtual function table is being cleaned up and will not be used, so virtual overloading is not available anymore at that time.
I've made some Stop() function, to be called before destruction.
0
 
jeroenrinsemaAuthor Commented:
Tanks Alex.
0
 
jeroenrinsemaAuthor Commented:
sorry
0
All Courses

From novice to tech pro — start learning today.