[cpp-threads] Re: Thread API interface tweaks
Howard Hinnant
hinnant at twcny.rr.com
Wed Aug 30 01:55:09 BST 2006
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.
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);
Peter's design adds an extra start-function wrapper around even what
thread sees, necessitating a different launcher. That appears key to
having futures be non-intrusive.
If we're very concerned about this, we could possibly do away with
factory functions/classes and put the launching functionality back
into thread/future (as Beman has favored all along):
thread<int> t(g);
future<int> f(g);
The downside of the lack of factory functions/classes is the loss of
being able to make OS-specific settings once, and use those settings
to launch many threads (stack size, scheduling, priority, etc.).
Another effect is that future<R> is no longer constructible from a
thread<R>:
thread<int> t = ...
future<int> f(t); // fails at compile time. Do we care?
I'm unsure of the importance of this conversion.
On Aug 29, 2006, at 8:43 AM, Nick Maclaren wrote:
> Well, I can think of a fair number of uses for void futures, so it
> would be more than just surprising if they are banned.
My chief motivation for avoiding future<void> was to keep from
imposing an efficiency penalty on thread<void>. With a completely
non-intrusive implementation of future, my objection to future<void>
wanes.
I note that this implementation of future does not expose a thread-
id. I think that is a good thing. I can see where a shared (const)
view of a thread's return value is valuable. But I get nervous about
shared ownership of a thread of execution where each owner has non-
const access to that thread of execution (e.g. the ability to change
a thread's priority). As far as I can tell, Peter's solution is safe
in this department. This is analogous to our collectively-learned
lesson that a reference counted design is good for an immutable
string, but not good for a mutable string.
On Aug 29, 2006, at 1:25 PM, Herb Sutter wrote:
> So future<T> should be implicitly convertible to future<void> if
> I'm not interested in the specific result
<nod> That's a very interesting conversion. I prototyped it
yesterday on my future's proposal after Beman pointed me in that
direction. I haven't prototyped it yet on Peter's proposal, but I
suspect it won't be much trouble.
> future_group<int> g1 = f1 || f2;
> future_group<void> g3 = ( f1 || f2 ) && f3;
Very interesting syntax suggestions, thanks.
On Aug 29, 2006, at 5:00 PM, Lawrence Crowl wrote:
> The distinction between class thread and class thread_id seems
> excessively
> complex. Why should we have thread at all? Why not just thread_id?
> The storage can be deallocated after completion of the join operation,
> which is essentially equivalent to new/delete.
The motivating use case for me came while trying to implement a
recursive_mutex adaptor (similar in spirit to a std::stack or
std::queue - container adaptors). It looks something like:
template <class Mutex>
class recursive_mutex
{
private:
Mutex& mut_;
unsigned count_;
thread_id id_;
public:
explicit recursive_mutex(Mutex& mut)
: mut_(mut), count_(0) {} // thread_id default constructed
to "not a thread"
void lock()
{
thread_id self = thread_id::current();
if (count_ > 0 && id_ == self)
++count_;
else
{
mut_.lock();
id_ = self;
store_release(&count_, 1);
}
}
...
};
I.e. recursive_mutex<Mutex> takes any exclusive Mutex and turns it
into a recursive mutex. To implement this I needed the notion of
thread id. But thread, which can refer to exclusive ownership of a
thread of execution, and which may need to contain more data than a
simple id, is overkill for this application.
On Aug 29, 2006, at 5:00 PM, Lawrence Crowl wrote:
> Futures are a very valuable facility, too valuable to be tied to
> threads.
> They should be a separate facility that happens to also work with
> threads.
That sounds very interesting to me. Got a prototype? I'll start
working, but I don't trust myself to correctly implement this
suggestion. It would be good to have more than one prototype to look
at.
On Aug 29, 2006, at 5:00 PM, Lawrence Crowl wrote:
> We already have a keyword __thread, and I think that defining a type
> named thread will lead to confusion. The joiner terminology at least
> does not have that problem.
Agreed. Although I'm not as sure of the resolution. We could rename
either __thread or thread. E.g. __thread_local.
-Howard
More information about the cpp-threads
mailing list