Boost logo

Boost :

From: William E. Kempf (williamkempf_at_[hidden])
Date: 2002-08-06 12:10:36


----- Original Message -----
From: "Peter Dimov" <pdimov_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Tuesday, August 06, 2002 11:37 AM
Subject: Re: [boost] Re: Re: Threads & Exceptions

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

But it's not. It's created before the thread is created, and POSIX memory
rules ensure proper visibility within the thread, and it's only accessed
after join(), which again, POSIX memory rules ensure proper visibility for.
If there were a need for accessing it after creation but before join() then
a mutex would be required here as well, but this is hardly surprising or
results in undefined behavior. I think maybe you were trying to stretch for
an argument here, as I can see the potential for misuse/mistakes that the
encapsulation of the return value in the thread state would eliminate.

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

On that I can agree, but I'd have preferred you stated it this way to begin
with ;). From the basis of this argument, I'm not sure it's a strong one.
The above idiom will be used in code even if the return value syntax is
adopted, so users already have to be aware of the rules of memory visibility
here.

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

Yes, however, you don't combine thread calls in the same manner in which you
combine function calls, and out values really aren't that unnatural to most
people. In C it's the only way to return more than one value, and even C++
users often do this instead of returning a compound type.

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

Which could be wrapped, but I get the point. I'm mostly swayed for return
value semantics, but I still don't agree with passing exceptions in this
manner.

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

If you can devise a method that relies only on copy construction and allows
reference types I'd love to see it. I'd have no objections at that point.

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

How can they be caught in the joining thread if terminate() is called, as
the standard says it must be?

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

True, so the issues are related and both issues are terrible, IMHO, and are
the crux of why I don't think it's appropriate to propogate exceptions out
of a thread.

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

I don't follow the special case of thread<void> thinking.

Bill Kempf


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