Boost logo

Boost :

Subject: Re: [boost] [thread_pool] Dependencies between tasks
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2008-09-06 09:02:28


----- Original Message -----
From: <k-oli_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Saturday, September 06, 2008 8:39 AM
Subject: Re: [boost] [thread_pool] Dependencies between tasks

>
> Am Samstag, 6. September 2008 06:40:03 schrieb vicente.botet:
>> ----- Original Message -----
>> From: "Kowalke Oliver (QD IT PA SI)" <Oliver.Kowalke_at_[hidden]>
>> To: <boost_at_[hidden]>
>> Sent: Friday, September 05, 2008 8:46 AM
>> Subject: Re: [boost] [thread_pool] Dependencies between tasks
>>
>> > sequencing the access to the instance would be equivalent to chaining
>> > the
>> > tasks (== chained tasks are executed after each other -> as a
>> > sequence).
>> > take a look at thread_pool at the vault
>> > http://www.boostpro.com/vault/index.php?action=downloadfile&filename=boos
>> >t-threadpool.2.tar.gz&directory=Concurrent%20Programming&.
>>
>> Do you mean that each time the user wants to sequence tasks she/he needs
>> * chain the task to the stored one
>> * store the last task
>
> I don't know what you mean with store a task.

Well, in order to chain a task a need to have a reference to the task. This
is what I meant by store a task.

> signature of chained_submit:
>
> template<
> typename Act,
> typename T
> >
> task< typename result_of< Act() >::type > chained_submit(
> Act const& act,
> task< T > & t);
>
> The function object act which should be passed to the thread pool is
> chained
> to the task t. This means - the function object act will be executed by I
> worker thread if the task t was finished.

OK, this allows us to chain tasks. Is this function thread_safe?

> struct A
> {
> void f( std::string const& str)
> { printf("A::f(): %s\n", str.c_str() ); }
> };
>
> struct B
> {
> void g()
> { printf("B::g()\n"); }
> };
>
> A a;
> B b;
>
> tp::pool<
> tp::fixed,
> tp::unbounded_channel< tp::fifo >
> > pool( tp::max_poolsize( 5) );
>
> tp::task< void > t1(
> pool.submit(
> boost::bind(
> & A::f,
> a,
> "abc",
> 2) ) );
> tp::task< void > t2(
> pool.chained_submit(
> boost::bind(
> & A::f,
> a,
> "efg",
> 1),
> t1) );
> tp::task< void > t3(
> pool.chained_submit(
> boost::bind(
> & A::f,
> a,
> "hij",
> 0),
> t2) );
>
> pool.submit(
> boost::bind(
> & B::g,
> b) );
>
> t3.get_future().wait();

This works well as soon as we have a reference to the task t1 when we submit
the task t2, and t2 when we submit the task t3. The storage of the last_task
submited to the pool for a given instance is needed when the code is not
linear but cyclic and the problem appears when these tasks have different
types. Suppose that the function called return different types, std::string
and std::size.

class A {
std::string f( ) {
    return "***********";
 }

std::size g( ) {
     return 5;
}
};

A a, b;

void h( ) {
     // do a expensive task
}

Image also that a function f will be called cyclically with values that
depends on the user input

while () {
    // get an int from cin in variable v
    f(v);
}

This function will depending onthe parameter submit different actions, but
no action can be done in parallel for the same instance
void f(int i) {
    switch (i) {
        case 1:
            tp::task<std::string> t1(
                pool.chained_submit(boost::bind(& A::f,a),
                                                    last_task));
            last_task = t1;
            // forward t1to other program logic needing the result of a.f()
            break;
        case 2:
            tp::task<std::size> t2(
                pool.chained_submit(boost::bind(& A::g,a),
                                                    last_task));
            last_task = t2;
            // forward t2 to other program logic needing the result of
a.g()
            break;
        case 3:
            pool.submit(h);
            break;
        // other cases ...
        }
    }

How last_task can be declared and initialized? As a task can be constructed
only by submiting a function to a pool we can submit a function that do
nothing.

    void do_nothing( ) {
    }

    tp::task<???> last_task(pool.submit(do_nothing));

What would be the type of last_task that provides the following functions:
            tp::task<???>(tp::task<void>);
            template<typename Act, typename T>
            tp::task< typename result_of< Act() >::type > chained_submit(
                  Act const& act,tp::task<???> & t);
            tp::task<???>& operator=(tp::task<std::size>);
            tp::task<???>& operator=(tp::task<std::string>);

The first function is matched by void. As chained_submit do not use at all
the value stored on the future of the task, a void task coul be enough also,
but we need a means to copy tp::task<T> on a tp::task<void>.

I think that the future library allows already something like that, i.e. a
future<void> can be used to wait for a future<T>. Do you think that this
void specialization for task<void> can be added to your library? Another
idea, why not define the chained_submit with a future instead of a task as
parameter?

>> I had already recovered your compressed file as other post let know.
>> Please
>> could you point me where the chaining task is described?
>
> example_chained_submit and in the docu chapter 'Submiting Tasks'

Sorry, I was seen an older documentation of the threadpool library. I see it
now.

Vicente


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk