Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2002-08-06 11:37:51


From: "William E. Kempf" <williamkempf_at_[hidden]>
> From: "Peter Dimov" <pdimov_at_[hidden]>
> > 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?

If the "main" thread (where "result" is defined) accesses "result" while
it's being updated by "foo", the behavior is undefined by the POSIX memory
rules.

The "other" design doesn't have this problem since the result variable is
hidden.

I'm not saying that this is a major problem, but there is some additional
safety to be gained.

> > > 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.

What's more important here is that "int foo()" is much more natural than
"void foo(int &)" (and the ability to combine function calls in expressions
is a major reason for this.)

If you already had a "foo" that you needed to execute in this manner, odds
are that it would have the "int foo()" form, not the "void foo(int &)" form.

> The crux of why I chose not to support return types was the requirement
for
> default constructability of R in the implementation.

I think I see where this requirement comes from... but I still suspect that
there might be a way to implement it using only the copy constructor (and
make it work for reference types.) I may be wrong. Even if this turns out to
be impossible, the restricted version is still better than nothing. :-)

> > 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.

terminate() is invoked when there is no matching exception handler. If
join() magically transports exceptions across thread boundaries, the
exception thrown in main() will be caught by the join()ing thread, and
things are fine and dandy.

Unfortunately, when there is no join(), the uncaught exception must
terminate the process, but we don't know in advance whether there will be a
join() sometimes in the future!

We can special case thread<void> to terminate on exceptions, of course, but
a consistent specification would be better.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk