[cpp-threads] Re: Thread API interface tweaks

Peter Dimov pdimov at mmltd.net
Wed Aug 30 21:13:05 BST 2006


Howard Hinnant wrote:
> I'm about 24 hours behind in this thread, my apologies.  I'm posting
> back in part just to catch up and confirm my understanding...
>
> On Aug 28, 2006, at 1:59 PM, Peter Dimov wrote:
>> Here's my future<> sketchy implementation for reference:
>
> Thanks Peter, that really helped.  I'm beginning to understand (I'm a
> slow learner :-)).  Here future<R> is truly a *non-intrusive* wrapper
> around thread<void>, unlike the partially intrusive wrapper I laid
> out.  I really like that it is completely non-intrusive.  This means
> it is not possible for it to impact the lower level thread launching
> efficiency.
>
> I don't think your design specifies that thread can't return a type.
> It only specifies that future<R> builds on thread<void> instead of
> thread<R> like I had.  So *if* we want thread to be able to return
> non-void types (and I strongly suspect we might), we can still do
> that with your design.

In principle, yes, we can. future<R> builds on anything that can execute 
asynchronously, and it will be usable with thread<void>, should we decide to 
have one.

But the other part of my design that I haven't shown yet does specify that 
threads don't return values and represent simply threads of 
control/execution.

I was actually a proponent of thread<R> once, so it's not a NIH syndrome; I 
just realized that the motivation for having thread<R> is simply that I was 
missing the future<R> component.

My current proposal for threading API is:

namespace thread
{

class handle;
// DefaultConstructible (NULL), CopyConstructible, Assignable
// EqualityComparable
// LessThanComparable, Hashable
// OutputStreamable (for diagnostic purposes)

template<class F> handle create( F f );

void join( handle th );
// pre: th != 0
// effects: blocks
// post: the thread identified by th has ended
// throws: cancellation exception

bool try_join( handle th );
bool timed_join( handle th, timespec const & tm ); // or boost::xtime, or 
whatever

handle current();

// Cancellation still under discussion, controversial
//
// void cancel( handle th );
// void set_cancel_state( handle th, bool st );
// void test_cancel();

// Yield/sleep do not belong to a high-level thread API, but there's popular 
demand...
//
// void yield();
// void sleep( timespec const & tm );

}

As an example in favor of separating return values and threads, consider the 
case of a thread pool. On startup, it creates (say) eight threads. When a 
task is submitted, a free thread is assigned to this task and a future is 
returned to the caller (see pooled_executor in my first post.)

The key here is that you don't know the return type in advance; the same 
thread can be used to execute tasks with different return values.

> Some downsides though:
>
> The syntax for launching a thread, and launching a future are
> different, e.g.:
>
> thread<int> t = launch_thread(g);
>
> future<int> f = threaded_executor().execute(g);
>
> The above is probably more different than it needs to be, but still,
> with Peter's (I think superior) proposal, at the very least we are
> looking at:
>
> thread<int> t = launch_thread(g);
> future<int> f = launch_future(g);

I just want to add here that the syntactic complexity in

> future<int> f = threaded_executor().execute(g);

is caused by the fact that threaded_executor conforms to the Executor 
concept. In practice, one would probably use

future<int> f = ex.execute(g);

where ex would model Executor but not be hardcoded as a threaded_executor().

The Executor concept corresponds to Kevlin's threader, with the difference 
that its models are not primitives, but can be implemented on top of the 
supplied primitives.

When one just needs a background thread per task, the syntax is indeed 
somewhat clumsy. We can wrap it with a convenience function, as you state 
above:

> future<int> f = launch_future(g);

but the caller will usually be able to choose a more efficient Executor 
based on g's level of parallelism.

Of course if we can implement a semi-magical function that will outperform 
70% of people's hand-coded thread pools, we ought to provide it:

    future<int> f = active( g );

;-)




More information about the cpp-threads mailing list