[cpp-threads] Proposing a layered Thread API

Peter Dimov pdimov at mmltd.net
Sun Sep 3 13:08:48 BST 2006


Ion Gaztañaga wrote:

> //Parameter can be passed by move semantics, no extra allocation
> future<std::string> f = thread_pool(bind(to_upper_case, std::move(s));
>
> //The asynchronous function ends, and constructs the result
>
> //The result is copied to s
> s = f();
>
> Apart from this, that can be negligible with common types, the fact
> that you can use future as many times as you want from the same
> thread, ...

Or from several different threads. The typical case is for one producer and 
one consumer to keep copies, but a future also supports several consumers 
waiting for the same result. (Multiple producers may or may not be 
supported, see below.)

> ... leads to the illusion that future is very similar to a
> synchronous value, which is not true, because:
>
> -> Each time you call f(), you create a copy

A future is similar to a synchronous value, actually; if you have a value v, 
every

    s = v;

statement does create a copy. It doesn't model a nullary function object 
very well, and there is something to be said in favor of just using f.get() 
and giving up the 70% genericity on the basis of not achieving full 100%.

Nevertheless, I favor the f() syntax simply because it's short.

> -> Each time you call f(), you need to hold a mutex (copy constructor is 
> not thread-safe,
> unless it's trivial or we explicitly say it with concepts)

You are right that a lock-free future<R>::operator() requires basic thread 
safety from the copy constructor of R. This is a reasonable requirement; 
basic thread safety will (should?) be the default throughout the standard 
library, much the same way as basic exception safety is the default. Under 
the C++ (and POSIX) memory model, most types provide basic thread safety 
"out of the box". These that don't typically have further problems that make 
them hard to use in more than one thread.

There is one design decision that can affect the ease of implementing a 
lock-free future: whether it is allowed to call set_value twice. I believe 
that even the harder variation where multiple set_value calls are allowed 
can be made lock-free, but this is somewhat beyond my limits of 
understanding. :-)

We also need to be careful not to compare apples to oranges. operator() can 
block. thread<R>::operator() needs to join. Joining a thread (when it's a 
call to the kernel) can be more expensive than locking a mutex (can be done 
in user space when uncontended.)

(Nothing prevents thread<R>::operator() from being implemented the same way, 
of course, but then there'd be no difference in performance.)




More information about the cpp-threads mailing list