waiting on multiple signals - pthread

Started by
4 comments, last by the_edd 12 years, 6 months ago
I am building a simple job system that would allow me to create a job graph with dependencies... Something like:


Job root;
Job job1;
Job job1_1;
Job job1_2;
Job job2(&job1, 1); // job2 cannot start until job1 finishes.

job1.addJob(&job1_1);
job1.addJob(&job1_2);
root.addJob(&job1);
root.addJob(&job2);

root.execute(); // execute the job graph.

I am using pthreads to implement this but I am new to this type of programs and I cant find a way to synchronize all this.

I tried using pthread_join to do something like this :


void Job::execute()
{
for(int i = 0; i < numDependencies; ++i)
dependencies.join(); // calling pthread_join

for(int i = 0; i < numSubJobs; ++i)
subJobs.start(); // calling pthread_create

... do some work here

for(int i = 0; i < numSubJobs; ++i)
subJobs.join(); // calling pthread_join
}

But my program crash in one of the pthread_join call.


[font="Verdana, Arial, Helvetica, sans-serif"]The results of multiple simultaneous calls to pthread_join() specifying the same target thread are undefined.[/font]
[/quote]

How can I achieve the same results but without the crash?

Thanks
Advertisement
You can use condition variables (though I'm not sure if pthread condition variables let you wait on them _after_ they've been pulsed, in which case we'd like thme to return immediately, so you might have to make your own kind on top of pthread condition variables), but in such a use case you'd have each job have its own i_am_finished condition variable, and have each job wait on each of its prerequisites' condition variables before starting.

Another option is to change the API a bit and to have a thread that's in charge of waiting for certain threads to finish and starting threads that may start.
phtread_join is good for waiting for a thread being terminated. This is used to wait for child threads.

If a parent thread starts its child threads at the moment when having completed the own task, then there is no need for the child threads to wait for anything. So why not using something like the following?

void Job::execute()
{
... do some work here

for(int i = 0; i < numSubJobs; ++i)
subJobs.start(); // calling pthread_create

for(int i = 0; i < numSubJobs; ++i)
subJobs.join(); // calling pthread_join
}

(This isn't necessarily the best solution but resembles the OP's structure.)
Parent threads can't start their child threads, and child threads can't wait on their parent threads, because the jobs form a DAG and not a tree.
The main problem with my code is that "pthread_join" is called twice on "job1". Once in "job2" dependency loop and once in "root" subJobs loop.

This is why my program crashes. (I did confirm it by removing the pthread_join call in the "root" subJobs loop).

If only pthread_join could be called multiple times it would fix my problem but I have to find another way to do this.

I may change my thread class so that my own join function would check if the thread has finished working before calling pthread_join.

Pseudo code:

class Thread
{
void create()
{
pthread_create(&tread, ...., threadFunc, ...);
done = false;
}

void join()
{
if(!done)
pthread_join(&tread);
}

static void threadFunc(impl)
{
impl->run();
impl->done = true;
}

virtual void run() = 0;
}



I bet I need to protect my "done" variable to make this thread safe... but surrending it with mutex didnt work so well.
Use condition variables they're designed for this kind of thing.

It might also be a good idea to phrase things in terms of tasks, rather than threads. If you had a concept akin to futures, you could also use the call-graph itself as your graph, rather than explicitly arranging a graph:


future<int> result1 = async_call<int>(functor1, arg1, arg2);
future<double> result2 = async_call<double>(functor2, arg3, arg4);

return async_call<bool>(functor3, result1, result2);


async_call would enqueue a task in a threadpool, probably. future<T> might having a conversion operator to a const T& that blocks until the return value is ready (and perhaps also helps the threadpool with some of its task processing).

This topic is closed to new replies.

Advertisement