|
Boost : |
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk