|
Boost : |
From: bill_kempf (williamkempf_at_[hidden])
Date: 2002-01-17 11:52:14
--- In boost_at_y..., Lee Brown <lee_at_a...> wrote:
>
> > I really don't think you should. Most of what you've had to say
has
> > been beneficial. I think that sometimes you're too stuck on POSIX
> > and not willing enough to try and envision how things should be
for a
> > language that POSIX truly doesn't cover, but your knowledge of
POSIX
>
> What about being stuck on exceptions? A lot of C++
> programmers do not use exceptions, because they increase code size
> dramatically. Are we to think they shouldn't have access to a C++
API for
> thread cancelation?
I'm not stuck on exceptions, I'm stuck on the requirement that the
stack is unwound. Any threading API that provides a mechanism that
terminates a thread with out unwinding it's stack is unusable in C++.
I say this with authority because this is precisely the model we have
in Win32. Applications that terminate threads with out unwinding the
stack at best leak resources, and more frequently cause serious bugs
or even crashes.
The POSIX folks even agreed with this. They designed pthreads so
that exceptions could be used. Further, there are many pthread
implementations that do use exceptions to implement cancellation
today. So your concern for code size is truly unfounded, since on
many platforms you get the increase in code size today.
> Furthermore....
>
> I believe, throw is like setting "errno" and then using a "goto"
statement.
No, it's not. Exceptions are much more sophisticated then this. If
they weren't, there'd be negligible added code size and you'd have
even less of a complaint here.
> If this is true, the "C++ stack" is actually the lowly "goto"
statement.
> i.e.
>
> some_func() {
> Widget m;
>
> if (true) throw 1;
>
> m=2;
> }
>
> is equivalent to:
>
> some_func() {
> struct c_widget m;
> c_widget_init(&m);
>
> if(true) {
> errno = 1;
> goto cleanup1;
> }
>
> m=2;
>
> cleanup 1 :
> c_widget_exit(&m);
> }
This doesn't take into account propagation up the stack. We don't
just clean up the resources called by some_func(), but also those
from some_other_func() that called some_func() and so on up the stack.
> A thread can be canceled in the middle of a throw.
It won't be able to be in Boost.Threads. Nor do I expect this to be
the case in a C++ standard threading library.
> Yes, the code they call saves us
> a lot of trouble but it is code that may never be executed.
> In a cancel enabled envirionment we can not use goto statements to
restore
> resources, we must use cancel_push and cancel_pop.
That's true in C, but that's a burden that simply won't be accepted
by C++ users. A simple example:
void foo()
{
auto_ptr<int> i(new int(2));
boost::thread::test_cancel();
}
If the call to test_cancel() actually determines the thread should be
cancelled then the destructor for the auto_ptr simply must be
called. If you require the C++ developer to instead use
cancel_push/pop() in MT code then he must rewrite a LOT of his code
in a tedious, error prone, non-natural style, that WILL bloat his
code. Worse, he may not always be able to blend the two approaches,
or he may be unable to change library code that wasn't written with
such a foreign mechanism in mind.
> It seems to me POSIX threads are correct. If cancelation is to be
implemented
> it would probably be wise to move towards their model with a more
handsome
> and convenient API. This API would also assist in covering up any
ugly
> implementations details imposed by less sophisticated platforms.
POSIX was correct, the allowed (but did not require, which leaves the
poor C++ developer with a huge problem in portability) cancellation
to be implemented with exceptions. You'll also find it impossible to
convince C++ programmers that cleanup_push/pop() is "a more handsome
API" then constructors/destructors.
Bill Kempf
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk