views:

217

answers:

3

Let's have the following class definition:

CThread::CThread ()
{
    this->hThread  = NULL;
    this->hThreadId  = 0;
    this->hMainThread = ::GetCurrentThread ();
    this->hMainThreadId     = ::GetCurrentThreadId ();
    this->Timeout  = 2000; //milliseconds
}

CThread::~CThread ()
{
    //waiting for the thread to terminate
    if (this->hThread) {
     if (::WaitForSingleObject (this->hThread, this->Timeout) == WAIT_TIMEOUT)
      ::TerminateThread (this->hThread, 1);

     ::CloseHandle (this->hThread);
    }
}

//*********************************************************
//working method
//*********************************************************
unsigned long CThread::Process (void* parameter)
{

    //a mechanism for terminating thread should be implemented
    //not allowing the method to be run from the main thread
    if (::GetCurrentThreadId () == this->hMainThreadId)
     return 0;
    else {
                m_pMyPointer = new MyClass(...);
                // my class successfully works here in another thread
     return 0;
    }

}

//*********************************************************
//creates the thread
//*********************************************************
bool CThread::CreateThread ()
{

    if (!this->IsCreated ()) {
     param* this_param = new param;
     this_param->pThread = this;
     this->hThread = ::CreateThread (NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, (void *)(this_param), 0, &this->hThreadId);
     return this->hThread ? true : false;
    }
    return false;

}

//*********************************************************
//creates the thread
//*********************************************************
int CThread::runProcess (void* Param)
{
    CThread* thread;
    thread   = (CThread*)((param*)Param)->pThread;
    delete ((param*)Param);
    return thread->Process (0);
}

MyClass* CThread::getMyPointer() {
    return m_pMyPointer;
}

In the main program, we have the following:

void main(void) {
  CThread thread;
  thread.CreateThread();

  MyClass* myPointer = thread.getMyPointer(); 
  myPointer->someMethod(); // CRASH, BOOM, BANG!!!!
}

At the moment the myPointer is used ( in the main thread ) it crashes. I don't know how to get the pointer, which points to memory, allocated in another thread. Is this actually possible?

+9  A: 

The memory space for your application is accessible to all threads. By default any variable is visible to any thread regardless of context (the only exception would be variables declared __delcspec(thread) )

You are getting a crash due to a race condition. The thread you just created hasn't started running yet at the point where you call getMyPointer. You need to add some kind of synchronization between the newly created thread and the originating thread. In other words, the originating thread has to wait until the new thread signals it that it has created the object.

Rob Walker
You are absolutely right, and I'm absolutely sleeping :) Thank you very much! I'll post the corrected code as an answer below.
m_pGladiator
A: 

As Rob Walker pointed out - I really missed the race condition. Also the crash is not when getting the pointer, but when using it.

A simple wait did the job:

MyClass* myPointer = thread.getMyPointer(); 

while (myPointer == 0) 
{
    ::Sleep(1000);
}

myPointer->someMethod(); // Working :)
m_pGladiator
Whilst adding the Sleep will appear to fix the problem, it is still a latent bug. There is no guarantee that the thread will be kicked off, and run to the right spot within 1s. The only guarantee would require an explicit signal between threads.
Rob Walker
Actually, in this specific case you _could_ spin (with a small sleep) waiting for the returned pointer to be non-zero. Not a recommended pattern in general, but it would make it safer, and in general faster than an explicit sleep.
Rob Walker
Using a condition variable instead is going to faster. Plus your added code doesn't even work, you need to call getMyPointer in the loop. Even then, without a mutex lock or volatile on m_pMyPointer there is no guarantee it will work.
Greg Rogers
A: 

I'm trying to get my head around what you are trying to do. It looks overly complicated for something like a thread-class. Would you mind post the class-definition as well?

Start by removing the C-style cast of the process-argument to CreateThread():

this->hThread = ::CreateThread (NULL, 0,&runProcess, (void *)(this_param), 0, &this->hThreadId);

If this doesn't compile you're doing something wrong! Never ever cast a function pointer! If the compiler complains you need to change your function, not try to cast away the errors! Really! You'll only make it worse for yourself! If you do it again they* will come to your home and do ... Let's see how you like that! Seriously, don't do it again.

Btw, in Process() I think it would be more appropriate to do something like:

assert(::GetCurrentThreadId() == hThreadId);

But if you declare it private it should only be accessible by your CThread-class anyway and therefor it shouldn't be a problem. Asserts are good though!

*It's not clear who they are but it's clear whatever they do it won't be pleasant!

Andreas Magnusson