I'm currently using boost::thread, because it very conveniently allows me to pass an arbitrary number of arguments to the thread and copies them along the way, so I don't have to worry about them being deleted before the thread launches. Is there any other library that allows this, or a way to simulate it using pthreads? I'd like to wean myself off of boost, but I've never seen any other library do this.
You would have to create a wrapper around pthread_create and pass in thread specific storage to pthread_create that contains an array of arguments, for example. The wrapper would use form such as:
void *mythreadfunction(void *arg) {...}
pthread_create_wrapper(context ctx, ...) {
Array *arr;
pthread_t mythread;
arr = new Array();
// push arguments to array here
blah blah ...
// create thread and pass in argument list as thread data pointer.
pthread_create(&mythread, NULL, mythreadfunction, (void *)arr);
}
I don't remember the details of Boost.Thread, but the general idea is something like this:
class thread_function_base
{
public:
virtual ~thread_function_base(void) {}
virtual void run(void) = 0;
};
template <typename Func>
class thread_function_0 : public thread_function_base
{
public:
thread_function_0(const Func& pFunc) :
mFunc(pFunc)
{}
void run(void)
{
mFunc();
}
private:
Func mFunc;
};
template <typename Func, typename A0>
class thread_function_1 : public thread_function_base
{
public:
thread_function_1(const Func& pFunc, const A0& pA0) :
mFunc(pFunc),
mA0(pA0)
{}
void run(void)
{
mFunc(mA0);
}
private:
Func mFunc;
A0 mA0;
};
// and so on to some limit, either
// generated either by hand (yuck), by
// Boost.PP (phew), or by C++0x's
// variadic templates (yay, no limit either)
class thread
{
public:
template <typename Func>
thread(const Func& pFunc)
{
std::auto_ptr<thread_function_base>
threadFunc(new thread_function_0<Func>(pFunc));
create_thread(threadFunc);
}
template <typename Func, typename A0>
thread(const Func& pFunc, const A0& pA0)
{
std::auto_ptr<thread_function_base>
threadFunc(new thread_function_1<Func, A0>(pFunc, pA0));
create_thread(threadFunc);
}
// again, needs to be generated somehow
private:
// noncopyable
thread(const thread&);
thread& operator=(const thread&);
// signature needs to match implementations expectations:
static void thread_function(void* pUserData)
{
std::auto_ptr<thread_function_base>
pFunc(static_cast<thread_function_base*>(pUserData));
// (A)
pFunc->run();
}
void create_thread(std::auto_ptr<thread_function_base>& pThreadFunc)
{
// again, implementation specific function:
if (create_thread(&thread_function, pThreadFunc.get(), ...))
{
// failed, do something (and return),
// auto_ptr in constructor will free resources
return;
}
// thread was created, so it now owns that resource
pThreadFunc.release();
// (B)
}
};
Basically, everything needed to invoke the thread is copied into some dynamically allocated container, a pointer to that dynamic container is passed into the thread function (trivial), then ownership is transferred from outside the thread to inside.
You can make things safer by packing not only the thread_function_base
into the user data, but also a (implementation-specific) signal handle. The threading function will block at (A)
until the signal is raised at (B)
, indicated the main thread has given the worker thread full ownership of the resources. (And from there it's auto_ptr
will eventually delete it.)
And so on, making it more sophisticated.