Click to See Complete Forum and Search --> : Help with creating a thread class using pthreads


vr84
July 29th, 2007, 11:09 AM
I'm trying to create a thread class using pthreads but I've run into some errors. When I compile my class and program I get a a lot of errors including:

Ran this command on a solaris machine:
g++ Thread.cpp -lpthread ThreadTest.cpp -lpthread

Thread.cpp: In member function `int Thread::CreateThread()':

Thread.cpp:21: error: argument of type `void*(Thread::)()' does not match `void*(*)(void*)'

Thread.cpp: At global scope:

Thread.cpp:25: error: prototype for `void* Thread::Run(void*)' does not match any in class `Thread'

Thread.h:13: error: candidate is: void* Thread::Run()


This is the contents of my header Thread.h:

#include <pthread.h>

class Thread
{
public:
Thread();
Thread(void *);
int CreateThread();
~Thread();
private:
pthread_t myThread;
void *var;
void *Run(void*);
};
---------------------------------------------------------

This is the contents of my Thread.cpp class:

#include "Thread.h"
#include <iostream>

using namespace std;

void *var;
pthread_t myThread;

Thread::Thread()
{
var = NULL;
}

Thread::Thread(void *x)
{
var = x;
}

int Thread::CreateThread()
{
return !( pthread_create(&myThread, NULL, Thread::Run, var) );
}

void* Thread::Run(void *x)
{
cout << "I'm inside the thread!!!";
pthread_exit(NULL);
}

Thread::~Thread()
{

}
-------------------------------------------------------------------

This is the contents of my programm ThreadTest.cpp:

#include <iostream>
#include "Thread.h"
#include <pthread.h>

using namespace std;

int main()
{
int x = 5;
Thread t( (void*)x );
t.CreateThread();
pthread_exit(NULL);
return 0;
}

I've only started learning c++ recently so I don't know where the bulk of the error is, in syntax or implementation. Any help would be appreciated.

JVene
July 29th, 2007, 12:52 PM
First things first.....

pthread_create is a C level function, which means it can't accept, as a pointer to a function, a member function. That's problem one.

What's required, to get over this problem, and indeed to end up calling a member function of your thread class (a desirable design), is that you must use a C function that calls the member function on an instance of your thread class.

I applaud your goal of creating a thread class - I long ago made mine, back when OS/2 was new, and it's one of the most valuable components of my library. Here's what I do:

I have a function I call InitiateThread, thus (abbreviated):



void * InitiateThread( void *p )
{
Thread *tp = ( Thread *) p;

p->Run();

pthread_exit( NULL );
}




In my thread class, the constructor will end up calling a member StartThread which does something like:



bool Thread::StartThread()
{
thread_id = pthread_create( &p_thread, NULL, InitiateThread, (void *)this );

return true; // the real one does a little more here
}




In my own class, Run is a virtual function, and Thread is a base to several versions which have specific uses.

The most useful is a QueProcessor, which owns a list (a queue) of objects representing member function pointers or other 'tasks' to be performed in order. The QueProcessor waits on an auto reset event object until signaled (when something is submitted).

I also have a QuePool, which owns a queue like QueProcessor, but represents a collection of threads which are dynamically created/destroyed according to timing rules. Generally it starts with 2 threads, and if pending entries in the queue aren't serviced within a time threshold, new threads are launched. As the number of threads increase, the time threshold is increased, until a maximum number of active threads is reached (at which point no new threads are launched). This means that if the queue services some long running tasks, other short running tasks are processed and released without being held.

QueProcessor has a template function that accepts a pointer to an instance and a member function to be called, using a 'Proc' object (a template class) to hold parameters for the call.

You'll probably want to cause the destructor of your thread class(es) to wait if the thread is still running, and perhaps deploy some means of signaling a cancellation request. This implies that the Run process indicates it's status to the thread class (running, stopped, waiting, etc).

It's also helpful to wrap the Run call inside an 'try/catch' block, so you can depend on the thread object continuing to operate properly even if the task crashes.

TheCPUWizard
July 29th, 2007, 01:10 PM
JVene,

That is a good technique. I have actually Generalized it....
(psuedo code)

class IObjectMethodCallBack
{
public:
void Execute(void *cData ) = 0;
}

template<CLASSNAME, PARAMTYPE>
class ObjectMethodCallBack : IObjectMethodCallBack
{
public:
typedef void(*CLASSNAME::MemberCallBack)(PARAMTYPE const &param, void * cData)
ObjectMethodCallBack(MemberCallBack method, PARAMTYPE const &param) : m_Method(method), m_Param(param) {}
void Execute(void *cData)
{
m_Method(m_Param, cData);
}
}

void CallBackHandler(void *p)
{
IObjectMethodCallBack callback = reinterpret_)cast<IObjectMethodCallBack>(p);}
callBack->(null);
}


Now we have a simple class hierarchy that can call any method of any class, provided it takes 2 parameters (the first can by of any type, the second is a void *).


CallBackHandler handles all of the management from a "C" like environment to the hierarchy.

If people are interested in this, I might just write an article (and give real functional code...)

JamesSchumacher
July 29th, 2007, 03:50 PM
JVene,

That is a good technique. I have actually Generalized it....
(psuedo code)

class IObjectMethodCallBack
{
public:
void Execute(void *cData ) = 0;
}

template<CLASSNAME, PARAMTYPE>
class ObjectMethodCallBack : IObjectMethodCallBack
{
public:
typedef void(*CLASSNAME::MemberCallBack)(PARAMTYPE const &param, void * cData)
ObjectMethodCallBack(MemberCallBack method, PARAMTYPE const &param) : m_Method(method), m_Param(param) {}
void Execute(void *cData)
{
m_Method(m_Param, cData);
}
}

void CallBackHandler(void *p)
{
IObjectMethodCallBack callback = reinterpret_)cast<IObjectMethodCallBack>(p);}
callBack->(null);
}


Now we have a simple class hierarchy that can call any method of any class, provided it takes 2 parameters (the first can by of any type, the second is a void *).


CallBackHandler handles all of the management from a "C" like environment to the hierarchy.

If people are interested in this, I might just write an article (and give real functional code...)

I take this approach.


namespace XCodeLib
{
class XCODELIB_CLASS IDelegate
{
public:
virtual XCODE_CALL ~IDelegate() throw();
// Interface Methods
virtual void XCODE_CALL Invoke(void * lpArgs) throw() = 0;
virtual const void * XCODE_CALL GetCallbackObject() const throw() = 0;
virtual const void * XCODE_CALL GetCallbackFunction() const throw() = 0;
};

template <typename Type,typename ArgType> class TDelegate : public IDelegate
{
public:
// Construction/Destruction
XCODE_CALL TDelegate(Type * pObject,void (Type::*pfnCallback)(ArgType *)) throw();
virtual XCODE_CALL ~TDelegate() throw();
// IDelegate
virtual void XCODE_CALL Invoke(void * lpArgs) throw();
virtual const void * XCODE_CALL GetCallbackObject() const throw();
virtual const void * XCODE_CALL GetCallbackFunction() const throw();
protected:
// Member Variables
Type * CallbackObject;
void (Type::*CallbackFunction)(ArgType *);
};

class XCODELIB_CLASS Event
{
public:
// Construction/Destruction
XCODE_CALL Event() throw();
virtual XCODE_CALL ~Event() throw();
// Member Functions
bool XCODE_CALL AddHandler(IDelegate * pDelegate) throw();
bool XCODE_CALL RemoveHandler(IDelegate * pDelegate) throw();
void XCODE_CALL Clear() throw();
void XCODE_CALL Invoke(void * lpArgs) throw();
protected:
// Member Variables
IDelegate ** EventHandlers;
unsigned long Length;
unsigned long Capacity;
// Member Functions
bool XCODE_CALL ReAlloc(unsigned long dwNewLength) throw();
};
}


Implementation not supplied, but interfaces shown I think you get the idea. :cool: It's native C++ Event Handling. Using this method, I would just have to create a static member function or a global callback to handle the thread callback, and the parameter I would just pass the IDelegate.


DWORD __stdcall ThreadProcess(void * lpDelegate)
{
IDelegate * pDelegate = reinterpret_cast<IDelegate *>(lpDelegate);

pDelegate->Invoke(NULL);

return 0;
}

int main(int,char **)
{
XCodeLib::TDelegate<MyClass,void> * pDelegate = new XCodeLib::TDelegate<MyClass,void>(somePointer,&MyClass::SomeMethod);

// Pass the delegate to the CreateThread function
// ...

return 0;
}

vr84
July 29th, 2007, 04:34 PM
Thanks that was a huge help, I added the Initiate function to call my class method and it works fine now.

TheCPUWizard
July 29th, 2007, 04:44 PM
James,

Show Off ;) (thats what I get for posting psuedo code off the top of my head rather than my real interfaces. :o

The only thing I do question (which really deserves a thread of it's own) is the use of the "throw()" qualifier. One of the three following conditions will be true.

1) None of the code that is called by the callback is capable of throwing any exceptions. [this can not be guaranteed].

2) Your implentation files (not shown) "eat" all exceptions. This can only be done with a "catch(...)", which is generally considered a bad design.

3) The code is going to violate the conditions ot "throw()".

I really dont believe any of these conditions are really "acceptable" [my opinion], which is why I do not use "throw()" in any form.

Your thoughts?

exterminator
August 22nd, 2007, 01:00 PM
I really dont believe any of these conditions are really "acceptable" [my opinion], which is why I do not use "throw()" in any form.So you are saying you never have written a function with a no-throw guarantee? Atleast you would have created exception classes from std::exception and overridden the virtual member what(). Haven't you? Moreover, if a function just uses C APIs, one can rest assured that it won't throw an exception and the user has to deal with other error handling techniques. If the implementation is handling those error conditions and translating them into exceptions, that is a different thing. Another common one is the destructor!

Some other standard examples, that give no-throw guarantee are: global operator delete/delete[], all member functions of numeric_limits (if I am not missing anything), no-throw operator new/new[], set_new_handler, and there are many more.

I don't think there is anything bad in specifying that I have a function f() that has a no-throw guarantee, that does not allow any exceptions or throw any exceptions. Could be perfectly reasonable. But of course sometimes such members can be harder to write and can cause ugly sucking up of the exceptions rather than letting them pass on to the caller. But those are mistakes done by people who usually mis-use exceptions.