Archived

This topic is now archived and is closed to further replies.

WebsiteWill

Threads Question

Recommended Posts

I have a class that when started will spawn a certain number of threads (depending on input at object istantiation). The class has a function called StartThreads. Inside of there I am calling the CreateThread method. I am having trouble getting CreateThread() to recognize the thread procedure I am passing to it. Here is the function call. cBaseServer::threadProc is of course a member of the class and the function I need the thread to use.
hThreadHandle = CreateThread(NULL, NULL,				        (LPTHREAD_START_ROUTINE)                       
                 &cBaseServer::threadProc, 		         sSharedThreadMemory,NULL, dwThreadID);
threadProc is defined as such
void* Recv(void* vp);
The void* parameter passed to threadProc is just temporary. I have a struct member in the class that I will pass in as a parameter, but I seem to get even more errors when I do not pass a void* to the actual function. I have read that because C++ is so strongly typed that it is difficult (but possible) to create a thread within an object. I know it can be done, I have just not found a snipet of code showing the exact method of casting. Any help on this one? Thanks, Webby

Share this post


Link to post
Share on other sites
That won''t work unless threadProc is static, and perhaps not even then... It will have issues if there is a this pointer being passed implicitly to the threadproc If you''re doing this to make some kind of server that has to receive from multiple clients, have you looked at the select() function? It''s a no-threads way to do it.

--------------------


You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming

You are unique. Just like everybody else.

"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com

Share this post


Link to post
Share on other sites

class foo
{
public:
foo();
int EventLoop();
static void ThreadLauncher( void* a_foo );
};

foo::foo()
{
// Hypothetical threading library...
CreateThread( foo::ThreadLauncher, (void*)this )
}

int foo::EventLoop()
{
while( 1 ) { }
}

void foo::ThreadLauncher( void* a_foo )
{
((foo*)a_foo)->EventLoop();
}

The constructer here creates a thread. The two parameters are the initial function for the thread to start in, and a parameter to pass to that function. The constructor chooses to pass a pointer to the object being constructed (this). ThreadLauncher knows that the void* passed is really a foo, so it casts it and then calls EventLoop.

For safety, you could add a dynamic_cast<> to ThreadLauncher() to make sure you''re really looking at a foo*.

Tony

Share this post


Link to post
Share on other sites
I''ve looked at select and it won''t work for what I am trying to do. There has to be a way to do this. Makes no real sense why it wouldn''t be allowed by the language. It''s just a matter of casting correctly.

I''m going to attempt the recommendation above.
Thanks,
Webby

Share this post


Link to post
Share on other sites
The way to do this is to use the lpParameter parameter in the CreateThread function to get back to your object. A little klunky but it works just fine:

Foo* pfoo1, pfoo2;
// ...
CreateThread( ..., ThreadDispatch, reinterpret_cast< LPVOID >(pfoo1), ...);
CreateThread( ..., ThreadDispatch, reinterpret_cast< LPVOID >(pfoo2), ...);

DWORD WINAPI ThreadDipatch( LPVOID lpParameter ) {
Foo* pfoo = reinterpret_cast< Foo* >(lpParameter);

pfoo->YourFunction();
);

At ThreadDispatch(), just cast the parameter back to a Foo and then call the function in the class that you want.

Regards,
Jeff

Edit: Fixed my angle brackets in the cast so they're visible

[edited by - rypyr on August 7, 2003 11:29:40 AM]

Share this post


Link to post
Share on other sites
I'm attempting what you suggested rypyr.
I have

DWORD WINAPI RecvDispatch(LPVOID lpParameter)
{
cBaseServer* pcBaseServer = reinterpret_cast<cBaseServer*>(lpParameter);
pcBaseServer->LocalRecv();
}

and I'm calling CreateThread like this.

CreateThread(NULL, NULL,
RecvDispatch, reinterpret_cast<LPVOID>
(pcBaseServer), NULL, dwThreadID);

It is still giving me this casting error:
error C2664: 'CreateThread' : cannot convert parameter 3 from 'unsigned long (void *)' to 'unsigned long (__stdcall *)(void *)'

It's got to be something simple at this point.

Webby
[/source]

[edited by - websitewill on August 7, 2003 11:54:45 AM]

Share this post


Link to post
Share on other sites
I know it''s ugly, but I bet a C-style cast would work like a charm reinterpret_cast always drives me nuts...

--------------------


You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming

You are unique. Just like everybody else.

"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com

Share this post


Link to post
Share on other sites
Still rather new to this casting everything-and-it''s-mother business. Can I get some simple syntax on what you mean by
C-style cast?

Webby

Share this post


Link to post
Share on other sites
He means this:

Blah blah;

void* pVoid = reinterpret_cast< void* >( &blah ); // C++ style cast

is the same as:

void* pVoid = (void*)(&blah); // C-style cast

They do the same thing, but one looks less ugly...

I'm not sure why you're getting that casting error for your dispatch function. It seems to not pick up the WINAPI declaration...

[edited by - rypyr on August 7, 2003 12:32:07 PM]

Share this post


Link to post
Share on other sites
Hmmm, you mention the WINAPI.
I seem to recall having to define something somewhere that is not done correctly in I think winbase.h
I''ll be darned is i can remember what it is.

I think it was this
#define WIN32_WINNT 0x0500#

Someone said tha WIN32_WINNT had to be defined as greater or equal to 0x0500

Maybe this has something to do with it? I remember not getting the hints on that page to work for me so I neglected to stick it in my favorites.

This familiar to anyone?

Webby

Share this post


Link to post
Share on other sites
Here is some example thread code from the MSDN Library.
With a few changes, this code works perfectly in a simple main program with the ThreadFunc() defined outside of main as usual.
However, if I wrap this into a class by making the default constructor to the tread setup and such as below and pass it the address of the ThreadFunc() as normal then I get the error stated previously:
C2664: 'CreateThread' : cannot convert parameter 3 from 'unsigned long (void)' to 'unsigned long (__stdcall *)(void *)'
I get this same error whether I call the thread function directly or if I go through the process of making the DispatchThread and calling the real thread through the parameter as in the example below.

Can anyone out there see the problem with this? It has to be a simple way of casting the function pointer but I'll be dashed if I can figure it out.

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

class ThreadClass
{
public:
ThreadClass();
DWORD WINAPI ThreadFunc();
DWORD WINAPI ThreadDispatch(LPVOID lpParameter);
};

ThreadClass::ThreadClass()
{
ThreadClass* pThreadClass;
DWORD dwThreadId;
HANDLE hThread;
char szMsg[80];

hThread = CreateThread(
NULL, // no security attributes

0, // use default stack size

ThreadDispatch, // thread function

reinterpret_cast<LPVOID>(pThreadClass),
//&dwThrdParam, // argument to thread function

0, // use default creation flags

&dwThreadId); // returns the thread identifier


// Check the return value for success.

if (hThread == NULL)
{
wsprintf( szMsg, "CreateThread failed." );
MessageBox( NULL, szMsg, "main", MB_OK );
}
else
{
_getch();
CloseHandle( hThread );
}
}

DWORD WINAPI ThreadFunc()
{
char szMsg[80];
DWORD lpParam = 1;
wsprintf( szMsg, "Parameter = %d.", *(DWORD*)lpParam );
MessageBox( NULL, szMsg, "ThreadFunc", MB_OK );

return 0;
}

DWORD WINAPI ThreadDispatch(LPVOID lpParameter)
{
ThreadClass* pThreadClass =
reinterpret_cast<ThreadClass*>(lpParameter);
pThreadClass->ThreadFunc();
}


NOTE: I get the same casting error either I reinterpret_cast a pointer to the class into a LPVOID and pass that in to CreatThread() as I do if I just simply pass it in normally.

Any help is certainly appreciated.

Webby

EDIT* Function name in above code. Still same error though.

[edited by - websitewill on August 7, 2003 6:11:34 PM]

Share this post


Link to post
Share on other sites
OK. I got it to work for now with a patch like this.
Using rypyr's code

Foo* pfoo1, pfoo2;
// ...
CreateThread( ..., ThreadDispatch, reinterpret_cast< LPVOID >(pfoo1), ...);
CreateThread( ..., ThreadDispatch, reinterpret_cast< LPVOID >(pfoo2), ...);

DWORD WINAPI ThreadDipatch( LPVOID lpParameter ) {
Foo* pfoo = reinterpret_cast< Foo* >(lpParameter);

pfoo->YourFunction();
);

except I make the function ThreadDispatch outside of the class.
It compiled. I'm sure is has bugs but I'm fixing to start running some tests on it in just a few minutes.

I would still like a way to have CreateThread accept a pointer to a function inside of the same class so that I can keep everything inside of once nice box. I'd also like to be able to make ThreadDispatch private so that outsiders cannot have access to it. Simply including it as private generated even more nasty errors I can not sort out at this time.

So, until I find more information of these casting problems I will have to deal with a partially closed black box.

I have found websites that say this sort of thing is possible but I'll be darned if I can make anything from the sites work for my benefit.
Here's the link if anyone is interested in helping.

[link]http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q94/5/79.ASP&NoWebContent=1[/link]

Until then,
Thanks for all the help,
Webby

[edited by - websitewill on August 7, 2003 8:08:17 PM]

Share this post


Link to post
Share on other sites
Ahh, no didn''t catch that. I saw the comment about static and while that did work I don''t think it would work correctly. So I guess I''ll be simply leaving the calling functions as global though I''d still rather not. But if there''s no other way then oh well.

I can go back to not needing the Dispatch function and can simply put my actual functions outside of the class and let CreateThread have access to them. I''ll just pass in the argument structure directly as well.

I guess it''s only a small break in the encapsulation. Essentially, the whole class I am creating will require no other user unput other than to create the object by passing in the desired parameters. After that it will function without no other input or access from the user. Just a simple declaration and voila, functioning network setup with however many reveiving threads as desired.

It works with a pool of worker threads to service incoming packets. I MIGHT later on add the functionality of having it sleep and wake threads as necessary based on an average number of incoming packets per unit time.

The only user input I may add later would be the ability to interactively add or remove threads to the pool at run time but I may not even need that functionality.

we''ll see.
Thanks for all the help,
Webby

Share this post


Link to post
Share on other sites
But you do understand that with the casting thing I showed you, you can get back to encapsulation, right? That is, you have a global callback that dispatches the thread execution to the object...

Share this post


Link to post
Share on other sites

template <typename ParentClass>
struct Thread
{
public:
typedef void (*fpLogFunc)(const TCHAR*const);
fpLogFunc logfunc;

static void noLog(const TCHAR*const){}
Thread() : logfunc(&Thread::noLog), m_hThread(INVALID_HANDLE_VALUE), m_dwID(0), m_pParentClass(0)
{
}

~Thread()
{
if(IsValid())
{
if(!IsTerminated())
{
//ASSERTMSG(0, "Thread object being destroyed prior to thread termination!\n");

this->logfunc(SC("Thread object being destroyed prior to thread termination!\r\n"));
Join();
}
CloseHandle(m_hThread);
m_hThread=INVALID_HANDLE_VALUE;
}
}

HANDLE handle(){return m_hThread;}

typedef unsigned int (ParentClass::*MethodProc)(void);
typedef Event<false, false> tyEvent;
tyEvent m_evExit;

bool Create(ParentClass* pThis, MethodProc pThreadMethod, const char* const name = 0)
{
//ASSERTMSG(INVALID_HANDLE_VALUE==m_hThread, "Don''t call Thread<>::Create without calling Close!");

if(INVALID_HANDLE_VALUE!=m_hThread)
{
this->logfunc(SC("Closing previously created thread due to second Thread<>::Create\r\n"));
this->Close();//return false;

}

m_evExit.Reset();
m_pParentClass = pThis;
m_pMethodProc = pThreadMethod;
//m_hThread = (HANDLE)_beginthreadex(NULL, 0, &DefaultProc, this, 0, (UINT*)&m_dwID);

m_hThread = CreateThread(NULL, NULL, &DefaultProc, this, 0, &m_dwID);

if( (0==m_hThread) || (INVALID_HANDLE_VALUE==m_hThread) )
return false;
else
return true;
}
int ThreadID() {return m_dwID;}
bool IsExiting()
{
return this->m_evExit.IsSignaled();
}
bool Exit()
{
return m_evExit.Signal();
}
bool Join(DWORD dwTime_ms=5000)
{
CLock<> AutoLock(m_csLock);

if(this->IsValid())
{
m_evExit.Signal();
if(this->WaitForTermination(dwTime_ms))
return true;
else
{
TCHAR szMsg[128];
tcscpy(szMsg, SC("* "));
tcscpy(&szMsg[tcslen(szMsg)], SC("Thread<> "));
tcscpy(&szMsg[tcslen(szMsg)], SC(" forcefully terminated\n"));
assert(tcslen(szMsg)<sizeof(szMsg));
this->logfunc(szMsg);
//Debug::Console.Out(szMsg);

return this->Terminate();
}
}
else
return true;
}

bool Close()
{
assert(INVALID_HANDLE_VALUE!=m_hThread);
//ASSERTMSG(IsTerminated(), "Terminate the thread prior to closing it''s handle!");

if(!IsTerminated())
{
this->logfunc(SC("Terminating thread on Thread<>::Close\r\n"));
Join(5000);
}
if(CloseHandle(m_hThread))
{
m_hThread=INVALID_HANDLE_VALUE;
return true;
}
else
return false;
}

bool IsValid() {return(INVALID_HANDLE_VALUE!=m_hThread);}
bool IsTerminated() {return (WAIT_OBJECT_0==WaitForSingleObject(m_hThread, 0));}
bool IsRunning() {return (WAITSCIMEOUT==WaitForSingleObject(m_hThread, 0));}

BOOL Suspend()
{
assert(INVALID_HANDLE_VALUE!=m_hThread);
return SuspendThread(m_hThread);
}

BOOL Resume()
{
assert(INVALID_HANDLE_VALUE!=m_hThread);
return ResumeThread(m_hThread);
}

enum ePriority
{
TimeCritical = THREAD_PRIORITY_TIME_CRITICAL,
Highest = THREAD_PRIORITY_HIGHEST,
AboveNormal = THREAD_PRIORITY_ABOVE_NORMAL,
Normal = THREAD_PRIORITY_NORMAL,
BelowNormal = THREAD_PRIORITY_BELOW_NORMAL,
Lowest = THREAD_PRIORITY_LOWEST,
Idle = THREAD_PRIORITY_IDLE
};

BOOL SetPriority(ePriority priority)
{
assert(INVALID_HANDLE_VALUE!=m_hThread);
return SetThreadPriority(m_hThread, priority);
}

bool WaitForTermination(DWORD dwTime_ms=INFINITE)
{
assert(INVALID_HANDLE_VALUE!=m_hThread);
return(WAIT_OBJECT_0==WaitForSingleObject(m_hThread, dwTime_ms));
}

protected:
static unsigned long __stdcall DefaultProc(void* pv)
{
if(pv==0) _asm{int 3}
assert(pv);
Sleep(10);
return reinterpret_cast<Thread*>(pv)->Run();
}
unsigned int Run()
{
try
{
assert(m_pParentClass);
assert(this);
assert(m_pMethodProc);
if(m_pParentClass && m_pMethodProc)
{
unsigned int x = (m_pParentClass->*m_pMethodProc)();
return x;
}
}
catch(...)
{
assert(0);//"Thread threw an uncaught exception"

throw;
}

return -3;
}
MethodProc m_pMethodProc;
ParentClass* m_pParentClass;

bool Terminate()
{
CLock<CCriticalSection> AutoLock(m_csLock);

if(INVALID_HANDLE_VALUE!=m_hThread)
if(TerminateThread(m_hThread, -42))
return true;
else
return false;
else
return true;
}

protected:
volatile HANDLE m_hThread;
DWORD m_dwID;
CCriticalSection m_csLock;
};



struct MyClassThatNeedsThreads
{
Thread thrAThread;
unsigned int ThreadFunction()
{
while(!this->thrAThread.IsExiting())
{
Sleep(10);
}
}

Initialize()
{
thrAThread.Create(this, &ThreadFunction);
}
};

Share this post


Link to post
Share on other sites