Sign in to follow this  
Shnoutz

waiting on multiple signals - pthread

Recommended Posts

I am building a simple job system that would allow me to create a job graph with dependencies... Something like:

[code]
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.
[/code]
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 :

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

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

... do some work here

for(int i = 0; i < numSubJobs; ++i)
subJobs[i].join(); // calling pthread_join
}
[/code]
But my program crash in one of the pthread_join call.

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

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

Thanks

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
phtread_join is good for waiting for a thread being terminated. This is used to wait for [i]child[/i] 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?
[code]
void Job::execute()
{
... do some work here

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

for(int i = 0; i < numSubJobs; ++i)
subJobs[i].join(); // calling pthread_join
}
[/code]
(This isn't necessarily the best solution but resembles the OP's structure.)

Share this post


Link to post
Share on other sites
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:
[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;
}

[/code]

I bet I need to protect my "done" variable to make this thread safe... but surrending it with mutex didnt work so well.

Share this post


Link to post
Share on other sites
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:

[code]
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);
[/code]

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).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this