Discussion:
Destructor called before startThread
(too old to reply)
Jack
2010-04-08 12:08:09 UTC
Permalink
Dear all,

First some code snippets,
//////////////////////////////////////////////////////////////////////////
class Thread
{
public:
Thread(std::auto_ptr<Runnable> runnable_);
Thread();
virtual ~Thread();
void start();
void *join();

private:
HANDLE hThread;
unsigned winThreadID;
std::auto_ptr<Runnable> runnable;
Thread(const Thread&);
const Thread& operator = (const Thread&);
void setCompleted();
void *result;
virtual void *run() { return 0; }
static unsigned WINAPI startThreadRunnable(LPVOID pVoid);
static unsigned WINAPI startThread(LPVOID pVoid);
void PrintError(LPTSTR lpszFunction, LPSTR fileName, int lineNumber);
};

Thread::Thread(std::auto_ptr<Runnable> runnable_) : runnable(runnable_)
{
if (runnable.get() == NULL)
PrintError("Thread(std::auto_ptr<Runnable> runnable_) failed at ",
__FILE__, __LINE__);
hThread = (HANDLE) _beginthreadex(NULL, 0, Thread::startThreadRunnable,
(LPVOID) this, CREATE_SUSPENDED, &winThreadID);
if (!hThread)
PrintError("_beginthreadex failed at ", __FILE__, __LINE__);
}

Thread::Thread() : runnable(NULL)
{
hThread = (HANDLE) _beginthreadex(NULL, 0, Thread::startThread,
(LPVOID)this, CREATE_SUSPENDED, &winThreadID);
if (!hThread)
PrintError("_beginthreadex failed at ", __FILE__, __LINE__);
}


unsigned WINAPI Thread::startThreadRunnable(LPVOID pVoid)
{
Thread *runnableThread = static_cast<Thread *>(pVoid);
runnableThread->setCompleted();
return reinterpret_cast<unsigned>(runnableThread->result);
}

//// Destructor called before this function is.
//// so the whole context gets destroyed
//// but why did that happen?
unsigned WINAPI Thread::startThread(LPVOID pVoid)
{
Thread *aThread = static_cast<Thread *>(pVoid);
aThread->result = aThread->run();
aThread->setCompleted();
return reinterpret_cast<unsigned>(aThread->result);
}

Thread::~Thread()
{
if (winThreadID != GetCurrentThreadId())
{
DWORD rc = CloseHandle(hThread);
if (!rc)
PrintError ("CloseHandle failed at ", __FILE__, __LINE__);
}
}

void Thread::start()
{
assert(hThread != NULL);
DWORD rc = ResumeThread(hThread);
if (!rc)
PrintError("ResumeThread failed at ", __FILE__, __LINE__);
}

void *Thread::join()
{
return result;
}

void Thread::setCompleted()
{
}
/////////////////////////////////////////////////////////////////////////////////////////

class BFThread : public Thread
{
public:
BFThread(int ID) : myID(ID) { }
virtual void *run()
{
return reinterpret_cast<void *>(myID);


}
private:


int myID;
};

//////////////////////////////////////////////////////////////////////////////////

// main module
std::auto_ptr<BFThread> thread1(new BFThread(1));

thread1->start();

int result1 = reinterpret_cast<int>(thread1->join());

///////////////////////////////////////////////////////////////////////////////

The errors are:
First-chance exception at 0x004177c7 in GridPartition.exe: 0xC0000005:
Access violation reading location 0xfeeefef2.
Unhandled exception at 0x004177c7 in GridPartition.exe: 0xC0000005: Access
violation reading location 0xfeeefef2.


I wrote the comments above. Could anyone please check my code why the
destructor is called before the startThread function is?
Thanks
Jack
Ulrich Eckhardt
2010-04-08 12:47:22 UTC
Permalink
Post by Jack
void *Thread::join()
{
return result;
}
This is missing a WaitForSingleObject() call to wait for the thread to
terminate.

Another comment: Take a look at Boost.Thread, which uses a slightly
different design. There, you don't derive from a thread class in order to
add your code to run in that thread. Similar to a file, its thread class is
just a means to communicate with a thread, but the lifetime of the thread
itself is independent thereof.

Also, it doesn't require any code to derive from a "Runnable" baseclass
either, it only requires the passed argument to be callable (e.g. a
function pointer or a class with overloaded operator()) and copyable (so it
can be given to the thread to start).

Lastly, if e.g. a passed pointer is incorrectly zero, I would either assert
or throw an exception. Just printing an error (PrintError) and continuing
isn't going to help.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Jack
2010-04-08 12:59:26 UTC
Permalink
Thanks Ulrich for sheding lights on this!!!
Jack
Jack
2010-04-09 10:40:41 UTC
Permalink
Hello,
Could I ask one further question before I move on...
I find out that the thread returns and exits
immediately after the run call.
Are there any ways to run the thread infinitely
without limit?
Thanks
Jack
Tim Roberts
2010-04-10 04:17:10 UTC
Permalink
Post by Jack
Could I ask one further question before I move on...
I find out that the thread returns and exits
immediately after the run call.
Are there any ways to run the thread infinitely
without limit?
The thread will run until your function returns, then it exits. If you
need it to do work, then code the thread so it does work.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Jack
2010-04-10 13:26:51 UTC
Permalink
Post by Tim Roberts
Post by Jack
Could I ask one further question before I move on...
I find out that the thread returns and exits
immediately after the run call.
Are there any ways to run the thread infinitely
without limit?
The thread will run until your function returns, then it exits. If you
need it to do work, then code the thread so it does work.
--
Hi Tim,
Where would be the best place to put the join call?
Thanks
Jack
Tim Roberts
2010-04-12 01:17:24 UTC
Permalink
Post by Jack
Post by Tim Roberts
Post by Jack
Could I ask one further question before I move on...
I find out that the thread returns and exits
immediately after the run call.
Are there any ways to run the thread infinitely
without limit?
The thread will run until your function returns, then it exits. If you
need it to do work, then code the thread so it does work.
Hi Tim,
Where would be the best place to put the join call?
The "join" concept comes from Linux pthreads. Basically, you do a "join"
whenever you need to know that the thread is finished. In Windows terms,
"join" is exactly like
WaitForSingleObject( hThread, INFINITE );

It simply blocks until the thread exits. So, the spot you wait depends on
when you need to know the thread is done.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Jack
2010-04-12 10:55:53 UTC
Permalink
Thanks tim, I pick it up now
Jack

Jack
2010-04-09 10:44:05 UTC
Permalink
Where should be the best place to put the join() call?
I tried WM_DESTROY, but it didn't run infinitely.
I tried to put it under the last statement of the method,
And the program just hanged. Any other options?
Thanks
Jack
Alexander Grigoriev
2010-04-08 14:32:29 UTC
Permalink
Are you by any chance using Thread object on the stack?
Post by Jack
Dear all,
First some code snippets,
//////////////////////////////////////////////////////////////////////////
class Thread
{
Thread(std::auto_ptr<Runnable> runnable_);
Thread();
virtual ~Thread();
void start();
void *join();
HANDLE hThread;
unsigned winThreadID;
std::auto_ptr<Runnable> runnable;
Thread(const Thread&);
const Thread& operator = (const Thread&);
void setCompleted();
void *result;
virtual void *run() { return 0; }
static unsigned WINAPI startThreadRunnable(LPVOID pVoid);
static unsigned WINAPI startThread(LPVOID pVoid);
void PrintError(LPTSTR lpszFunction, LPSTR fileName, int lineNumber);
};
Thread::Thread(std::auto_ptr<Runnable> runnable_) : runnable(runnable_)
{
if (runnable.get() == NULL)
PrintError("Thread(std::auto_ptr<Runnable> runnable_) failed at ",
__FILE__, __LINE__);
hThread = (HANDLE) _beginthreadex(NULL, 0, Thread::startThreadRunnable,
(LPVOID) this, CREATE_SUSPENDED, &winThreadID);
if (!hThread)
PrintError("_beginthreadex failed at ", __FILE__, __LINE__);
}
Thread::Thread() : runnable(NULL)
{
hThread = (HANDLE) _beginthreadex(NULL, 0, Thread::startThread,
(LPVOID)this, CREATE_SUSPENDED, &winThreadID);
if (!hThread)
PrintError("_beginthreadex failed at ", __FILE__, __LINE__);
}
unsigned WINAPI Thread::startThreadRunnable(LPVOID pVoid)
{
Thread *runnableThread = static_cast<Thread *>(pVoid);
runnableThread->setCompleted();
return reinterpret_cast<unsigned>(runnableThread->result);
}
//// Destructor called before this function is.
//// so the whole context gets destroyed
//// but why did that happen?
unsigned WINAPI Thread::startThread(LPVOID pVoid)
{
Thread *aThread = static_cast<Thread *>(pVoid);
aThread->result = aThread->run();
aThread->setCompleted();
return reinterpret_cast<unsigned>(aThread->result);
}
Thread::~Thread()
{
if (winThreadID != GetCurrentThreadId())
{
DWORD rc = CloseHandle(hThread);
if (!rc)
PrintError ("CloseHandle failed at ", __FILE__, __LINE__);
}
}
void Thread::start()
{
assert(hThread != NULL);
DWORD rc = ResumeThread(hThread);
if (!rc)
PrintError("ResumeThread failed at ", __FILE__, __LINE__);
}
void *Thread::join()
{
return result;
}
void Thread::setCompleted()
{
}
/////////////////////////////////////////////////////////////////////////////////////////
class BFThread : public Thread
{
BFThread(int ID) : myID(ID) { }
virtual void *run()
{
return reinterpret_cast<void *>(myID);
}
int myID;
};
//////////////////////////////////////////////////////////////////////////////////
// main module
std::auto_ptr<BFThread> thread1(new BFThread(1));
thread1->start();
int result1 = reinterpret_cast<int>(thread1->join());
///////////////////////////////////////////////////////////////////////////////
Access violation reading location 0xfeeefef2.
Unhandled exception at 0x004177c7 in GridPartition.exe: 0xC0000005: Access
violation reading location 0xfeeefef2.
I wrote the comments above. Could anyone please check my code why the
destructor is called before the startThread function is?
Thanks
Jack
Jack
2010-04-09 10:37:44 UTC
Permalink
Hi Alex,
Post by Alexander Grigoriev
Are you by any chance using Thread object on the stack?
Ummm.... I set out the thread as a local variable of the method, I vaguely
remember one of you said that although the var is on the stack, the thread
itself should be on the heap... no? correct me if I am wrong...
Thanks
Jack
Ulrich Eckhardt
2010-04-09 11:36:33 UTC
Permalink
Post by Jack
Post by Alexander Grigoriev
Are you by any chance using Thread object on the stack?
Ummm.... I set out the thread as a local variable of the method, I vaguely
remember one of you said that although the var is on the stack, the thread
itself should be on the heap... no? correct me if I am wrong...
Sorry, but the terms "on the stack" or "on the heap" don't apply to a
thread, just as you can't say that about a file. These are only meaningful
for C++ objects.

Now, concerning your class object, no, it is not on the stack. However, its
lifetime is bound to the auto_ptr object on the stack, which might lead to
problems if it is still used by the created thread while the creating
thread has already destroyed the object.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Jack
2010-04-09 12:52:02 UTC
Permalink
Thanks guys for your efforts!
Now my coding looks like this:

switch(uMessage)
{
case WM_CREATE:
hEvent = CreateEvent(NULL,FALSE,FALSE,"Test");
break;
case WM_COMMAND:
// the thread is starting to run here athread->run(); by a push of
a button
break;
case WM_DESTROY:
result1 = reinterpret_cast<int>(thread1->join());
PostQuitMessage(0);
break;
...

////////////////////////////////////////
I have removed the thread as a local variable and put it in the "global"
area.
like this

HANDLE hEvent;
std::auto_ptr<BFThread> thread1(new BFThread(1));

//////////////////////////////////////
I can see the thread run, but it terminates instantly.
Hello <<<<<<<<< output of the thread
The thread 'Win32 Thread' (0x718) has exited with code 1 (0x1).
The program '[3808] GridPartition.exe: Native' has exited with code 0 (0x0).
//////////////////////////////////

I consider it to be very wrong. But don't know how to handle it....
Could anyone lend me a hand?
Thanks
Jack
Loading...