$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: William E. Kempf (williamkempf_at_[hidden])
Date: 2002-08-06 11:17:46
----- Original Message -----
From: "Peter Dimov" <pdimov_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Tuesday, August 06, 2002 10:32 AM
Subject: Re: [boost] Re: Re: Threads & Exceptions
> From: "William E. Kempf" <williamkempf_at_[hidden]>
> > From: "Peter Dimov" <pdimov_at_[hidden]>
> > >
> > > Roll-your-own thread<R> is possible, but I definitely don't agree with
> its
> > > "triviality",
> >
> > void foo(int& res)
> > {
> > res = 10;
> > }
> >
> > int result;
> >
> > boost::thread thread(boost::bind(&foo, boost::ref(result))); // warning,
> > done from memory and likely wrong
> > thread.join();
> > std::cout << result << std::endl;
> >
> > I'd call this trivial.
>
> The fact that you can create undefined behavior by accessing "result"
aside,
> this is a toy example. As I said, when you deal with more than one thread,
> things aren't that simple, although you could still maintain that they are
> trivial.
Hmm... too early for this kind of thinking. What undefined behavior is
created by accessing "result" here?
> std::vector<int> results;
> std::vector< shared_ptr<thread> > threads;
>
> for(int i = 0; i < N; ++i)
> {
> results.push_back(0);
> threads.push_back(shared_ptr<thread>(new thread(bind(foo,
> ref(results.back())))));
> }
>
> std::for_each(threads.begin(), threads.end(), mem_fn(&thread::join));
>
> std::cout << std::accumulate(results.begin(), results.end(), 0) <<
> std::endl;
I don't see this as any more complex then the alternative, so yes I'd still
maintian that it's trivial.
(Please, don't assume my mind is made up here... I think my arguments have
come across that way often in the past and it's stifled the arguments
earlier then they should have been.)
> > Adding a value return to boost::function would only
> > allow illimination of two lines above (the declaration of "result" and a
> > merging of the join() call and the cout line), neither of which add
> anything
> > to the clarity of the code, and in fact would make the code less
readable,
> > IMHO.
>
> To put things in perspective:
>
> int foo()
> {
> return 10;
> }
>
> std::cout << boost::thread(foo).join() << std::endl;
>
> Less readable? OK, let's attribute this to taste.
Actually, yes, I think it is. Combining calls into a single statement in
this manner is a form of code obfuscation. Not that I don't use this style
myself and appreciate the compactness of it.
The crux of why I chose not to support return types was the requirement for
default constructability of R in the implementation. This results in cases
where the "out value" implementation is required any way, so I saw only
minor benefit to the return value approach. But I realize syntactic sugar
like this is appreciated by end users, so I'm being swayed.
> > > especially when a thread group (performing a computation in
> > > parallel) needs to be joined and the results combined.
> >
> > I don't see how the above implementation is any less trivial even in
this
> > case.
>
> I'm not going to debate the precise meaning of "trivial" in this context,
> but you could equally well argue that non-void returns are an unnecessary
> language feature, since you can trivially emulate them with out arguments.
Yes, you could. The return value is syntactic sugar only. I eschewed the
sugar in this case only because of the necessary type requirements. Maybe
not a strong argument from your (generic you) perspective, and I appreciate
that.
> > > The enumerated exception list is questionable, though. I'd specify
> join()
> > to
> > > throw an implementation-defined exception when f() throws. With
compiler
> > > magic, it can even be a copy of the real thing. Without, well, it can
at
> > > least preserve std::exception::what().
> >
> > Another issue here is, again, how this ties in with current exception
> > semantics. Though not necessarily a good application design, current
> > threading libraries allow you to join the "main" thread (and the best we
> > could do is prohibit this in documentation). As long as you can join
the
> > main thread we'll have to change the standards required behavior of
> > propogating an exception out of main(), and that would be something
tricky
> > to implement and obviously all implementations would be non-portable.
>
> Yes, a valid concern. This is perhaps the biggest problem with this
> proposal, what happens with the exception - if there is one - when join()
is
> never called.
Actually, that's the other argument I made in another post. In the above
argument I'm assuming that join() *is* called, but that it's called from
thread A on the main thread, which would invoke undefined behavior if an
exception were propogated out of main() in this manner.
Bill Kempf