Topic: C++ Dtors & Thread Cancellation.


Author: AllanW@my-dejanews.com
Date: 1999/02/17
Raw View
In article <36C2F250.7869@clipper.ens.fr>,
  Valentin Bonnard <bonnard@clipper.ens.fr> wrote:
>
> Stanley Friesen [Contractor] wrote:
>
> > Now, I do have an objection to this system: it adds to the cost of simple
> > operations even when the exception is not thrown.  This comes from the
> > need to do an actual check at every relevent sequence point.  Now commas
> > and semicolons cause code to be generated!!
>
> No, there is an alternative, with no runtime penalty until
> an exception is thrown: when an exception is thrown, trace
> the program until you reach a sequence point... ahem, no

The "... ahem, no" makes me think that you were joking. But this is the
closest I've yet heard to making the idea work.

There are problems with the idea, of course.

  * Not every CPU has built-in facilities for tracing a program.

  * On most CPUs I know of which DO have built-in facilities for
    tracing a program, the facility exists to give an extreme
    level of control and information to a debugger. Thus, the
    operations are likely to be very slow and very complex.

  * Using these facilities may interfere with use of a debugger.

    -- The solution to these three problems may be as simple as
       locating the next sequence point and inserting a call to
       a subroutine, but...

  * Some CPUs do not allow user-level code to modify "code"
    segments while they execute, and

  * How do you recognize when you've reached a sequence point?

Maybe what we need is to have the compiler emit some instruction
at sequence points, something along the lines of "if flag
EXCEPTION_PRESENT is set, jump to subroutine." But this is
equivalent to polling! Furthermore, even if this instruction were
the smallest and fastest one in the whole chip (comparable to a
no-op), it would still have *SOME* effect on the program (which
is now larger and at least slightly slower). The compiler might
minimize this (for instance, by combining adjacent sequence
points with no intervening side effects). Even so, we can't
completely eliminate the origional objection ("now commas and
semicolens cause code to be generated").

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/17
Raw View
Valentin Bonnard wrote:
>
> The alternative is:
>
> auto_ptr<Foo> p (new Foo);
> Foo* q = o.get ();
> p->Bar ();
>
> it should be safe, but I think there is a sequence point
> just before the call to auto_ptr<Foo> (Foo*), so it isn't
> really safe.

because auto_ptr is a half-way encapsulation of memory allocation.
I take the objection serious, though.  Such half-way solutions work
with the present exceptions, and in the case of auto-pointers, I
think `half-way' means `simple' rather than `bad'.

The term `sequence point' was used in the original suggestion,
because it represents the maximum number of places where we can do
polling without compromising _fully encapsulated_ resources.  But,
we might instead say,

  In a function which is declared to throw a signal exception,
  or in a try-block which catches such an exception, there is a
  polling point after the execution of a full statement.

  When a signal occurs, a signal exception is thrown at a later
  polling point.  [There is no requirement that this should be
  the next polling point after the signal occured.]

(This will also allow using the comma operator to prevent polling
in certain cases.)

The loseness of the second rule allows optimal implementations.

Another problem:  Would different signals be dispatched, or
would there be just one signal exception class?  Dispatching is
certainly good, but it will rise some questions how it is best
done without introducting ad-hoc features into the language.

Petter.

--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Dave Abrahams" <abrahams@mediaone.net>
Date: 1999/02/18
Raw View
In article <36CAEFC4.DF603F72@matfys.lth.se> , Petter Urkedal
<petter@matfys.lth.se>  wrote:

>   In a function which is declared to throw a signal exception,
>   or in a try-block which catches such an exception, there is a
>   polling point after the execution of a full statement.
>
>   When a signal occurs, a signal exception is thrown at a later
>   polling point.  [There is no requirement that this should be
>   the next polling point after the signal occured.]

This would still make some code which was otherwise exception-safe unsafe
when placed in any of the aforementioned contexts. Writing exception-safe
code is hard enough for most people without introducing more variables. I'd
like to see some evidence that:
1. A compiler can actually do a sufficiently good job at choosing where to
poll for exceptions.
2. Compiler-generated polling is significantly better than a programmer who
understands his code's usage patterns can do.
3. This advantage is important enough to warrant confusing the domain of
exception-safety.

-Dave
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <bonnard@clipper.ens.fr>
Date: 1999/02/15
Raw View
Dave Abrahams wrote:

> Let me say it much more simply; I'm sure you'll be able to appreciate it
> this way.
>
> Foo *p = new Foo;
> Foo *q = p; // danger
> try { p->Bar(); } catch(...) { delete p; throw; }
> delete p;
>
> This code is exception-safe, but if an exception can occur at the line
> marked "danger", it is not. Your proposal would make such an exception
> possible.

This code is low-level as it directly manipulate owning pointers.

The alternative is:

auto_ptr<Foo> p (new Foo);
Foo* q = o.get ();
p->Bar ();

it should be safe, but I think there is a sequence point
just before the call to auto_ptr<Foo> (Foo*), so it isn't
really safe.

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnard@clipper.ens.fr>
Date: 1999/02/15
Raw View
Dave Abrahams wrote:

> So, what happens when a function declared as throwing a signal exception
> destroys a vector? Is signal polling disabled?

It's disabled in any function which doesn't allow asyn exceptions, such
a
~vector.

> I think I understand your proposal, and as I understand it, it still seems
> unworkable. Even if you fixed the problems described above, given the small
> number of places where automatic polling might be inserted, you'd be better
> off doing it yourself.

I think I understand how it would be used.

In the implementation of classes, where you manipulate pointers,
ref count etc, any async exception would cause problems.

OTOH, using these classes is easier and safer; it's even safe
with async exceptions because a string will always be destructible.

So in class implementation, disable signal pooling. Otherwise
enable it. (That's oversimplified of course.)

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnard@clipper.ens.fr>
Date: 1999/02/15
Raw View
Stanley Friesen [Contractor] wrote:

> Now, I do have an objection to this system: it adds to the cost of simple
> operations even when the exception is not thrown.  This comes from the
> need to do an actual check at every relevent sequence point.  Now commas
> and semicolons cause code to be generated!!

No, there is an alternative, with no runtime penalty until
an exception is thrown: when an exception is thrown, trace
the program until you reach a sequence point... ahem, no

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnard@clipper.ens.fr>
Date: 1999/02/16
Raw View
Dave Abrahams wrote:

> Despite AlanW's mistaken notion of sequence points, his basic point still
> stands: if the compiler can introduce exceptions, it changes the nature of
> code about which we could otherwise make strong assumptions. I'm not sure it
> is actually possible to write exception-safe code in such environments. One
> can no longer compose two operations known not to throw without getting an
> operation that can throw.

Hum...

void foo () throw (signal, ...)
{
    throw (...) {
        a ();
        b ();
    }
    c ();
    d ();
}

(or, if this syntax isn't valid, introduce another function
for a(); b();)

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/11
Raw View

AllanW@my-dejanews.com wrote in message >How about:
>
>    void myFunc() throw(signal_detected) {
>        for (int i=0; i<1000000; ++i) {
>            // Important operations here...
>
>            if (signal_poll()) throw signal_detected;
>        }
>    }

I'd probably use a function check_for_signal() which would check signal_poll
and throw. Oh, and lose the exception-specification ;)

>Here we're only polling when the user asks us to. The signal will be
>"buffered" until the user polls for it. If the user never does poll
>for it, it's never detected.


That's fine with me. I fear it is the only answer that would be fine with
me. Any solution where the compiler inserts the polling will require a
different set of rules for reading and writing exception-safe code,
depending on the context. I don't like that idea.

-Dave




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/11
Raw View
Dave Abrahams wrote:
>
> >But these are sequence points within the source code of auto_ptr, or?
> >Or is my understanding of sequence points lacking?
>
> I think so. This transfer of ownership involves several function calls.
> There is a sequence point at each function call (see section 1.8, paragraph
> 17 in the standard).

There is a sequence point after evaluation of all arguments and before
calling the function, according to CD2 (don't believe it's changed).
So, you're right on this point.

However, it does not break the proposal.  A well written class
(like auto_ptr) shall leave its objects in a `good' state after each
call to a member function.  Thus, the object is in a good state at any
sequence point inside the expression which uses it.  The only place
the object may `break' is within its own member functions, where the
programmer of that class is responsible not to enable polling, or to
write exception safe code.

-petter.

--
[- http://matfys.lth.se/~petter/ -]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/11
Raw View
Petter Urkedal wrote in message <36C31EB6.A5CE2340@matfys.lth.se>...

>There is a sequence point after evaluation of all arguments and before
>calling the function, according to CD2 (don't believe it's changed).
>So, you're right on this point.
>
>However, it does not break the proposal.  A well written class
>(like auto_ptr) shall leave its objects in a `good' state after each
>call to a member function.  Thus, the object is in a good state at any
>sequence point inside the expression which uses it.  The only place
>the object may `break' is within its own member functions, where the
>programmer of that class is responsible not to enable polling, or to
>write exception safe code.

That's an oversimplified view. A legal auto_ptr implementation is allowed to
release its pointer to an auto_ptr_ref (whose destructor will not delete the
object it refers to) before ownership is assumed by the auto_ptr being
constructed.

Yes, I know the standard says that auto_ptr_ref "holds a reference to an
auto_ptr". It just turns out that the implementation of auto_ptr requires
some static_casts if you do it that way, and since the user can't tell what
auto_ptr_ref actually holds, by the "as-if" rule you can implement it
differently... unless the compiler is allowed to introduce exceptions where
there previously were none. This is probably not the strongest example, but
it should be enough to get the point across: your proposal to allow the
compiler to introduce exceptions where previously none were allowed can
break code which previously was exception-safe.

Let me say it much more simply; I'm sure you'll be able to appreciate it
this way.

Foo *p = new Foo;
Foo *q = p; // danger
try { p->Bar(); } catch(...) { delete p; throw; }
delete p;

This code is exception-safe, but if an exception can occur at the line
marked "danger", it is not. Your proposal would make such an exception
possible.

-Dave
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/12
Raw View

Dave Abrahams wrote in message <79vco6$md5$1@news.harvard.net>...
>Yes, I know the standard says that auto_ptr_ref "holds a reference to an
>auto_ptr". It just turns out that the implementation of auto_ptr requires
>some static_casts

<snip>

Oops, I meant const_casts, sorry!



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/10
Raw View
Dave Abrahams wrote:
>
> I'm still waiting to hear your proposal as to how a function will be
> "declared as throwing a signal exception".
>
I think you answered it below.  Anyway,

    #include <exception> // activates the (builtin) `signal_exception'

    void f(int i) throw(signal_exception);  // polls for signals

    void i() { /* signals NOT acknowledged here... */ }
    void h() {
 try {     // ...even if they are here,
     i();  // and we call i().
 } catch(signal_exception const&) { /*...*/ }
    }

> There are other functions which must not throw, for example, operations on
> STL iterators. It sounded like you were saying that they would be allowed to
> throw a signal exception just so long as there was a catch block somewhere
> up the call chain that would catch the exception.
>
> Okay, you're not saying that. So with regard to catch blocks, you're only
> proposing that polling could be automatically inserted by the compiler
> between the braces. Any function which was "declared as throwing a signal
> exception" would be subject to the insertion of automatic polling *within
> its body only*.

Yes, by `inside' I referred to the source code, not the flow of control.
Primary operations on iterators is usually fast, so they need not poll
for signals.  The main thing is to identify the parts of code that _may_
take measurable time (0.1 s?), and write it in an exception safe way.

Even without this proposal, we would be quite far by just having
library functions and an interface to system calls which transfers
a signal into an exception.  This would address at least the portion
of code which in itself is not time-consuming, but which calls
blocking or time-consuming library-functions.

> Okay, we might have to extend exception-specifications to allow this:
>
>     throw( signal_exception, ... )
>
> Otherwise, you would have to specify every exception that a function could
> throw if you wanted it to be able to throw signalling exceptions. There are
> serious maintainance and robustness problems associated with trying to get
> exception-specifications right. There are good reasons never to use them (or
> only to use empty ones).

Actually, don't use them myself ;-).  Yes I think the `...' syntax
would have to go along with it.

> But even this extension scares me. It would be all too easy for users (me!)
> to forget to write the ... part and cause unexpected program termination.

Or you may forget to initialize a pointer and get a segmentation fault?

> [...] A valid implementation of auto_ptr (written by its
> principal designer, Greg Colvin) transfers ownership by releasing the
> pointer and putting a copy in an auto_ptr_ref, then constructing the new
> auto_ptr using the auto_ptr_ref. As far as I can tell, there are several
> sequence points along the way. If an implementation were allowed to insert
> polling code in there, it would be easy to leak the pointer.

But these are sequence points within the source code of auto_ptr, or?
Or is my understanding of sequence points lacking?  AFAIK the places
where a statement may contain sequence points is at operators `,', `&&',
and `||', and maybe some others, but _not_ at `operator=' or in passing
an argument to a ctor.

-petter.

--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/10
Raw View
"Stanley Friesen [Contractor]" wrote:
>
> Now, I do have an objection to this system: it adds to the cost of simple
> operations even when the exception is not thrown.  This comes from the
> need to do an actual check at every relevent sequence point.  Now commas
> and semicolons cause code to be generated!!

But which sequence points the signal is polled for may remain
unspecified.  Thus, an efficient compiler is free to analyze code,
break up loops, and insert polling only where it cannot prove that
there is a sufficiently near `polling point'.

The term `sufficiently near' is depends on the what the signals are
used for.  If signals result from user interaction, polling at maximum
interval or 0.1 s seems reasonable.  Though the problem of selecting
the optimal `polling points' amounts to solving the halting problem,
I believe quite optimal selection is possible in most cases.  And since
0.1 s * 100 MHz = 100 000, the cost of the polling may be reduced
to a negligible fraction (like 1/100?).

-petter.

--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/10
Raw View

Petter Urkedal wrote in message <36C152CF.8B22269C@matfys.lth.se>...
>Even without this proposal, we would be quite far by just having
>library functions and an interface to system calls which transfers
>a signal into an exception.

You won't hear any argument from me about that.

>> Okay, we might have to extend exception-specifications to allow this:
>>
>>     throw( signal_exception, ... )
>>
>> But even this extension scares me. It would be all too easy for users
(me!)
>> to forget to write the ... part and cause unexpected program termination.
>
>Or you may forget to initialize a pointer and get a segmentation fault?


It's not the same kind of danger at all. Compilers can (and do) warn about
uninitialized variables. In any case an uninitialized pointer is much more
likely to be detected during testing than an incorrect
exception-specification.

>But these are sequence points within the source code of auto_ptr, or?
>Or is my understanding of sequence points lacking?

I think so. This transfer of ownership involves several function calls.
There is a sequence point at each function call (see section 1.8, paragraph
17 in the standard). Here's another simple example: the expression *++p,
where p is an STL iterator, is guaranteed to complete without throwing an
exception. If automatic polling at any sequence point were allowed, an
exception might occur after the increment but before the dereference, since
each of these operations could be implemented by a function.

-Dave




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1999/02/10
Raw View
Petter Urkedal wrote in message <36C0167A.2D7C7660@matfys.lth.se>...

>Exactly.  My message was probably a bit minimal.  To declare a function
>as throwing a signal exception shall mean that such an exception may be
>_implicitely_ thrown on any sequence point within it.


So you aren't asking for asynchronous exceptions to be thrown.

You're asking for a way to specify that synchronous checks for async
signals/exceptions be made at every sequence point in the text of a
particular function.  When such an async exception is detected, it would be
thrown synchronously.

For now it seems appropriate to use a pragma to enable such behavior.  I'm
probably naive, but I would expect such a requirement to be relatively rare.
You can get almost the same behavior with a small inline function, but you
lose some of the RTTI capability of normal exceptions.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: zivc@peach-networks.com (Ziv Caspi)
Date: 1999/02/10
Raw View
On 9 Feb 1999 21:00:41 GMT, AllanW@my-dejanews.com wrote:

[...Long discussion omitted...]
>
>My conclusion is that David Abrahams is right: if you can
>trigger an exception at "an arbitrary sequence point" (such as
>in an interrupt routine), all bets are off.

Your arguments (and conclusion) fail to take into account
that there *are* exception mechanisms (stronger than C++'s,
in fact) that do handle exceptions at arbitrary sequence points.
Consider SEH as found in OS/2 and Win32 (9x/NT).

The question is: Do these implementation necessarily require
OS support, or can they be incorporated into C++?

---------------------------------------------
Ziv Caspi
zivca@netvision.net.il
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/11
Raw View
Ziv Caspi wrote in message <36c13b3f.64270195@news.netvision.net.il>...
>Your arguments (and conclusion) fail to take into account
>that there *are* exception mechanisms (stronger than C++'s,
>in fact) that do handle exceptions at arbitrary sequence points.
>Consider SEH as found in OS/2 and Win32 (9x/NT).

First of all, these mechanisms don't handle exceptions, they produce them.
Handling the exception correctly is left up to the poor user, and might be a
much knottier problem.

Secondly, in user code, they can even cause exceptions between sequence
points (for example, a divide-by-zero which can occur in the middle of some
larger expression).

Third, SEH is usually only invoked if undefined behavior is invoked (e.g.
dereferencing a null pointer), so if we code carefully we shouldn't have to
worry about structured exceptions in our own code. Structured exceptions
from the OS should be wrapped up and converted to C++ exceptions.

Despite AlanW's mistaken notion of sequence points, his basic point still
stands: if the compiler can introduce exceptions, it changes the nature of
code about which we could otherwise make strong assumptions. I'm not sure it
is actually possible to write exception-safe code in such environments. One
can no longer compose two operations known not to throw without getting an
operation that can throw.

-Dave
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1999/02/11
Raw View
In article <79qa9f$43h@abyss.West.Sun.COM>,
  stanley@West.Sun.COM (Stanley Friesen [Contractor]) wrote:
> In article <79q6m8$ruh$1@nnrp1.dejanews.com>,  <AllanW@my-dejanews.com> wrote:
> >Now let's introduce hardware interrupts, or multiple
> >threads, or probably half a dozen other things I can't
> >think of at the moment, which interrupt the flow of
> >control in ways beyond the control of the compiler. ...
> >
> >But what if the interrupt routine throws an exception?
> >Here we perform any neccesary stack operations and
> >eventually reach the nearest catch block. global_ulong
> >is left in it's potentially invalid state.
>
> I think the proposal here dealt with this issue.

I missed the beginning of the thread. Sorry.

> The proposal would be something like this:
>
> 1. Interrupt sets a per-thread "exception" flag, and does NOT
> actually throw any excpetion.
>
> 2. At a *sequence* *point*  (which rules out the middle of an
> increment operation), in a function *explicitly* declared to
> throw the intr_exception, the compiler inserts a check of the
> thread's exception flag, and causes a throw if it is set.

That makes a lot of sense.

> Now, I do have an objection to this system: it adds to the cost of simple
> operations even when the exception is not thrown.  This comes from the
> need to do an actual check at every relevent sequence point.  Now commas
> and semicolons cause code to be generated!!

True, but there's no other realistic way to do it. And you
wouldn't get it on every function, only those that can trap
signal exceptions...

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1999/02/11
Raw View
In article <79sfgt$25g$1@news.harvard.net>,
  "Dave Abrahams" <abrahams@motu.com> wrote:
>
>
> Petter Urkedal wrote in message <36C152CF.8B22269C@matfys.lth.se>...
> >Even without this proposal, we would be quite far by just having
> >library functions and an interface to system calls which transfers
> >a signal into an exception.
>
> You won't hear any argument from me about that.
>
> >> Okay, we might have to extend exception-specifications to allow this:
> >>
> >>     throw( signal_exception, ... )
> >>
> >> But even this extension scares me. It would be all too easy for users
> (me!)
> >> to forget to write the ... part and cause unexpected program termination.
> >
> >Or you may forget to initialize a pointer and get a segmentation fault?
>
> It's not the same kind of danger at all. Compilers can (and do) warn about
> uninitialized variables. In any case an uninitialized pointer is much more
> likely to be detected during testing than an incorrect
> exception-specification.
>
> >But these are sequence points within the source code of auto_ptr, or?
> >Or is my understanding of sequence points lacking?
>
> I think so. This transfer of ownership involves several function calls.
> There is a sequence point at each function call (see section 1.8, paragraph
> 17 in the standard). Here's another simple example: the expression *++p,
> where p is an STL iterator, is guaranteed to complete without throwing an
> exception. If automatic polling at any sequence point were allowed, an
> exception might occur after the increment but before the dereference, since
> each of these operations could be implemented by a function.

So your point is that resolution to the nearest sequence point still
isn't coarse enough; while it protects the compiler's data structures,
there is no protection for the library or user data structures.

How about:

    void myFunc() throw(signal_detected) {
        for (int i=0; i<1000000; ++i) {
            // Important operations here...

            if (signal_poll()) throw signal_detected;
        }
    }

Here we're only polling when the user asks us to. The signal will be
"buffered" until the user polls for it. If the user never does poll
for it, it's never detected.

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/11
Raw View
Dave Abrahams wrote:

[...]

> >But these are sequence points within the source code of auto_ptr, or?
> >Or is my understanding of sequence points lacking?
>
> I think so. This transfer of ownership involves several function calls.
> There is a sequence point at each function call (see section 1.8, paragraph
> 17 in the standard). Here's another simple example: the expression *++p,
> where p is an STL iterator, is guaranteed to complete without throwing an
> exception. If automatic polling at any sequence point were allowed, an
> exception might occur after the increment but before the dereference, since
> each of these operations could be implemented by a function.

Then what about replacing the sequence point with the end of a
full expression? This point must be treated by the compiler anyway
(f.ex. to call destructors of temporaries), and it will not
interfere with expressions like *x++=*y++, since the whole thing
forms just one full expression (if it is not part of a larger
expression, of course).
This could even be used with the comma operator, to express
that in an pseudo-async-exception function, two function calls
or other expression statements should be "glued" together:

void f() throw(signal_exception, ...)
{
  foo(); // After this, a signal_exception may be thrown
  bar(), // The comma doesn't end the full expression: no exception
  baz(); // Here, the exception may be thrown.
}

Note that for more complicated cases, you can still make
an inline function:

inline void atomic()
{
  while(foo()) bar(); // no signal_exception is thrown here
}

void f() throw(signal_exception, ...)
{
  // ...
  atomic();
  // ...
}

However, maybe instead of declaring it on functions
(for the caller it doesn't matter, if the exception thrown
from f is "async" inside f; it's just a normal synchronous
exception from the caller's view), one makes a special signal
block:

signal(true)
{
  // here, signal_exception may be raised
}

The true is to allow no-signal blocks as well:

signal(true)
{
  // Here signal_exception may be raised
  signal(false)
  {
    // Here no signal_exception may be raised
  }
  // Here signal_exception may be raised again
}

The parameter should obviously a constant expression.

Probably "signal" is a bad name for the keyword.
However, I don't find a better one now.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/02/11
Raw View
AllanW@my-dejanews.com wrote:
>
> In article <79sfgt$25g$1@news.harvard.net>,
>   "Dave Abrahams" <abrahams@motu.com> wrote:
> >
> >
> > Petter Urkedal wrote in message <36C152CF.8B22269C@matfys.lth.se>...
> > >Even without this proposal, we would be quite far by just having
> > >library functions and an interface to system calls which transfers
> > >a signal into an exception.
> >
> > You won't hear any argument from me about that.
> >
> > >> Okay, we might have to extend exception-specifications to allow this:
> > >>
> > >>     throw( signal_exception, ... )
> > >>
> > >> But even this extension scares me. It would be all too easy for users
> > (me!)
> > >> to forget to write the ... part and cause unexpected program termination.
> > >
> > >Or you may forget to initialize a pointer and get a segmentation fault?
> >
> > It's not the same kind of danger at all. Compilers can (and do) warn about
> > uninitialized variables. In any case an uninitialized pointer is much more
> > likely to be detected during testing than an incorrect
> > exception-specification.
> >
> > >But these are sequence points within the source code of auto_ptr, or?
> > >Or is my understanding of sequence points lacking?
> >
> > I think so. This transfer of ownership involves several function calls.
> > There is a sequence point at each function call (see section 1.8, paragraph
> > 17 in the standard). Here's another simple example: the expression *++p,
> > where p is an STL iterator, is guaranteed to complete without throwing an
> > exception. If automatic polling at any sequence point were allowed, an
> > exception might occur after the increment but before the dereference, since
> > each of these operations could be implemented by a function.
>
> So your point is that resolution to the nearest sequence point still
> isn't coarse enough; while it protects the compiler's data structures,
> there is no protection for the library or user data structures.
>
> How about:
>
>     void myFunc() throw(signal_detected) {
>         for (int i=0; i<1000000; ++i) {
>             // Important operations here...
>
>             if (signal_poll()) throw signal_detected;
>         }
>     }
>
> Here we're only polling when the user asks us to. The signal will be
> "buffered" until the user polls for it. If the user never does poll
> for it, it's never detected.

But this is already possible today:

volatile sig_atomic_t signal_called = 0;

void signal_handler() // called on signal
{
  signal_called=1;
}

void myFunc()
{
  for(int t=0; i<1000000; ++i)
  {
    // Important operations here...

    if(signal_called) throw signal_detected;
  }
}


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: mikem@ar.ar.com.au (Mike Mowbray)
Date: 1999/02/07
Raw View
Re various replies to my original posting (some by email)...

1. The "solution" of writing the thread-to-be-cancelled such that it checks
   a flag now and then is only applicable to a subset of applications.
   It cannot be used with (say) a peripheral thread which blocks
   indefinitely reading a socket (where the canceller thread knows
   nothing of the socket itself). One is forced to use some kind of
   timed-out read()/select() instead, which distorts/complicates a
   design pattern which is otherwise simple and elegant. And even this may
   not be possible if the blocking call is deep inside a third-party
   library.

   This "solution" is also inapplicable in systems intended to exhibit
   long-running fault-tolerance, because a watcher thread cannot cancel
   recalcitrant misbehaving threads without fear of resource leakage.

2. Using signals to throw an exception is not a general solution,
   because you can never know for sure whether a particular signal might
   also be used for something else inside a third-party library.
   Further, such asynchronous exceptions are contrary to the whole
   spirit of deferred cancellation in pthreads.

It's important to remember some general remarks in Bjarne Stroustrup's
early writings: Although a particular design style may be "possible" in
a language, that's quite different from the language *supporting* that
design style.

Existing C++ compilers (such as Sun, DEC/Compaq, and others) which
implement reasonably-efficient exceptions, tightly coupled with pthread
cancellation for stack unwinding, show that such support is possible,
and without paying a high price. The sense of freedom one gains when
using a compiler which supports this has to be experienced to be
believed (much like O.O. programming itself, compared to plain
procedural programming).

All that's needed is codify/ratify/mandate support for this style of
multi-threaded C++ programming in the POSIX/ISO-C++ standards, so that
code developed using that style can be more widely portable.

--
Mike Mowbray  Internet: mikem@ar.com.au
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David Abrahams <abrahams@motu.com>
Date: 1999/02/07
Raw View
Petter Urkedal wrote:
>
> A general solution to the thread-cancellation problem is to provide
> exceptions upon signals, which thus builds on the already present
> (ANSI C) awarance of signals. However, we would have to make some
> restrictions, so that a function is not interrupted at an unexpected
> point (and leaving allocated resources).

Let's just get one thing straight from the get-go: any mechanism which
can cause an exception inside another thread at an arbitrary sequence
point (even if that sequence point were within an appropriate catch
block) would be an utter disaster for the thread being cancelled.

Exception-safety depends in large part on the knowledge that certain
operations do not throw. What happens in the std::vector<T>::~vector()
if an exception is thrown between the destructors of two of the Ts? The
exception might as well have come from T's destructor itself. The
standard requires the destructors of contained types not to throw for a
very good reason!

I can appreciate the attractiveness of using exceptions to cancel a
thread, but the exception absolutely must be thrown by the thread being
cancelled, from a place known to be benign. Polling for the cancellation
message, as suggested by a previous poster, would seem to be a
reasonable approach.

-Dave
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/08
Raw View
David Abrahams wrote:
>
> Petter Urkedal wrote:
> >
> > A general solution to the thread-cancellation problem is to provide
> > exceptions upon signals, which thus builds on the already present
> > (ANSI C) awarance of signals. However, we would have to make some
> > restrictions, so that a function is not interrupted at an unexpected
> > point (and leaving allocated resources).
>
> Let's just get one thing straight from the get-go: any mechanism which
> can cause an exception inside another thread at an arbitrary sequence
> point (even if that sequence point were within an appropriate catch
> block) would be an utter disaster for the thread being cancelled.

I think you missed the point.  First, it is possible to write
exception-safe blocks of code (in some places auto_ptr suffice, in
other places you may have to catch the exception, clean up, and
rethrow.)  Second, if can declare _which_ blocks exceptions
may occur in, it is the users responsibility to decide when he may
allow signal exceptions.

> Exception-safety depends in large part on the knowledge that certain
> operations do not throw. What happens in the std::vector<T>::~vector()
> if an exception is thrown between the destructors of two of the Ts?

But why would anyone declare std::vector<T>::~vector() (or any other
destructor) as throwing a signal exception?  And if you don't, no
exception of the kind will occur, according to the proposal.

> I can appreciate the attractiveness of using exceptions to cancel a
> thread, but the exception absolutely must be thrown by the thread being
> cancelled, from a place known to be benign. Polling for the cancellation
> message, as suggested by a previous poster, would seem to be a
> reasonable approach.

Put another way, my proposal was that such polling is inserted by
the compiler at selected sequence points in a function declared to
throw signal_raised, or in blocks which catch it.  The selection of
among the sequence point is done to improve performance, and may
involve braking up loops by an efficient compiler.

 -petter.

--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/08
Raw View
In article <36BEDF15.91EC076C@matfys.lth.se> , Petter Urkedal
<petter@matfys.lth.se>  wrote:

> David Abrahams wrote:
>>
>> Petter Urkedal wrote:
>> >
>> > A general solution to the thread-cancellation problem is to provide
>> > exceptions upon signals, which thus builds on the already present
>> > (ANSI C) awarance of signals. However, we would have to make some
>> > restrictions, so that a function is not interrupted at an unexpected
>> > point (and leaving allocated resources).
>>
>> Let's just get one thing straight from the get-go: any mechanism which
>> can cause an exception inside another thread at an arbitrary sequence
>> point (even if that sequence point were within an appropriate catch
>> block) would be an utter disaster for the thread being cancelled.
>
> I think you missed the point.  First, it is possible to write
> exception-safe blocks of code (in some places auto_ptr suffice, in
> other places you may have to catch the exception, clean up, and
> rethrow.)

I'm pretty sure I knew that already ;)

> Second, if can declare _which_ blocks exceptions
> may occur in, it is the users responsibility to decide when he may
> allow signal exceptions.
>
>> Exception-safety depends in large part on the knowledge that certain
>> operations do not throw. What happens in the std::vector<T>::~vector()
>> if an exception is thrown between the destructors of two of the Ts?
>
> But why would anyone declare std::vector<T>::~vector() (or any other
> destructor) as throwing a signal exception?  And if you don't, no
> exception of the kind will occur, according to the proposal.

Did you propose some way to declare that a particular function might throw a
signal exception? If so, I must have missed that.

So, what happens when a function declared as throwing a signal exception
destroys a vector? Is signal polling disabled?

What happens when the vector's destructor calls a function which is declared
as throwing signal exceptions? Is signal polling enabled?

What mechanism do you propose to use to declare that a function might throw
signal exceptions? Remember that exception-specifications are not checked
across compilation units (and have other serious problems, anyway).

>> I can appreciate the attractiveness of using exceptions to cancel a
>> thread, but the exception absolutely must be thrown by the thread being
>> cancelled, from a place known to be benign. Polling for the cancellation
>> message, as suggested by a previous poster, would seem to be a
>> reasonable approach.
>
> Put another way, my proposal was that such polling is inserted by
> the compiler at selected sequence points in a function declared to
> throw signal_raised, or in blocks which catch it.  The selection of
> among the sequence point is done to improve performance, and may
> involve braking up loops by an efficient compiler.

I think I understand your proposal, and as I understand it, it still seems
unworkable. Even if you fixed the problems described above, given the small
number of places where automatic polling might be inserted, you'd be better
off doing it yourself.

-Dave
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/09
Raw View
Dave Abrahams wrote:
>
> In article <36BEDF15.91EC076C@matfys.lth.se> , Petter Urkedal
> <petter@matfys.lth.se>  wrote:
> >
> > I think you missed the point.  First, it is possible to write
> > exception-safe blocks of code (in some places auto_ptr suffice, in
> > other places you may have to catch the exception, clean up, and
> > rethrow.)
>
> I'm pretty sure I knew that already ;)

Of course, my apologies.

> Did you propose some way to declare that a particular function might throw a
> signal exception? If so, I must have missed that.

Exactly.  My message was probably a bit minimal.  To declare a function
as throwing a signal exception shall mean that such an exception may be
_implicitely_ thrown on any sequence point within it.

> So, what happens when a function declared as throwing a signal exception
> destroys a vector? Is signal polling disabled?

Yes.  I think the confusion rises from the fact that by `inside a
function' (or try-block) I am referring to the _source code_, and
not the runtime _flow of control_ (or what's the term?).

> What happens when the vector's destructor calls a function which is declared
> as throwing signal exceptions? Is signal polling enabled?

But that is not any more problematic than when a destructor calls
a function which throws whatever else.  In practice destructors
tends to call destructors, and exceptions in destructors can be
avoided.  I have not encountered the problem yet, but I've seen that
others are concerned about it.

Further, when a destructor is called, it means that program flow is
already `going in the right direction' of cleaning up, so there is
no reson to poll for a signal exception before program flow starts
to go `in the wrong direction' again.

> What mechanism do you propose to use to declare that a function might throw
> signal exceptions? Remember that exception-specifications are not checked
> across compilation units (and have other serious problems, anyway).

Maybe I'm missing something here?  (What do you mean by `checked'?)
To call a function, it must be declared, and `throw' declarations
goes along with the declaration, so any compilation unit will see
it.  We must require _explicit_ specification of signal exceptions.

> I think I understand your proposal, and as I understand it, it still seems
> unworkable. Even if you fixed the problems described above, given the small
> number of places where automatic polling might be inserted, you'd be better
> off doing it yourself.

That depends on programming style.  A programmer which uses auto_ptr
or memory managed pointers, and encapsulates access to other
resources in classes may accept signal exceptions in most of his
functions.  Anyway, function do throw various exceptions today, so I
don't think the problem will be so radically more severe.

Petter

--
[- http://matfys.lth.se/~petter/ -]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1999/02/09
Raw View
In article <36BEDF15.91EC076C@matfys.lth.se>,
  Petter Urkedal <petter@matfys.lth.se> wrote:
> David Abrahams wrote:
> >
> > Petter Urkedal wrote:
> > >
> > > A general solution to the thread-cancellation problem is to provide
> > > exceptions upon signals, which thus builds on the already present
> > > (ANSI C) awarance of signals. However, we would have to make some
> > > restrictions, so that a function is not interrupted at an unexpected
> > > point (and leaving allocated resources).
> >
> > Let's just get one thing straight from the get-go: any mechanism which
> > can cause an exception inside another thread at an arbitrary sequence
> > point (even if that sequence point were within an appropriate catch
> > block) would be an utter disaster for the thread being cancelled.
>
> I think you missed the point.  First, it is possible to write
> exception-safe blocks of code (in some places auto_ptr suffice, in
> other places you may have to catch the exception, clean up, and
> rethrow.)  Second, if can declare _which_ blocks exceptions
> may occur in, it is the users responsibility to decide when he may
> allow signal exceptions.

With respect, I think it is you who missed the point.

In single-thread code which does not have any concept of
"interrupts", we can speak of exception-aware code. We can
say that code in a try block will handle *any* exception
that occurs, either in the block or in *any* code it calls
before the block is exited. But this is really just
notational convenience. In reality, there are only a few
places where exceptions can occur, and these are well-known.
At each of these places the code must be ready to react to
an exception, but between them no exception is possible and
the code can rely on this.

Consider, for instance, this code:

    ++global_ulong;

where global_ulong is a global unsigned long. On some
computers, the unsigned long is 4 bytes long, but the CPU
can only access 2 bytes at one time. On that computer, the
compiler will do something comparable to this:

    Add 1, global_ulong+0     ; #1
    Add OF, global_ulong+2    ; #2

Here we assume global_ulong+0 is the "low half" of the
unsigned long, and global_ulong+2 is the "high half."

If global_ulong is 0x4FFFE, then global_ulong+0 holds
0xFFFE and global_ulong+2 holds 0x4. When this code is
executed, #1 changes global_ulong+0 to 0xFFFF and
changes CPU flag OF (OverFlow) to 0. Then #2 has no
effect, because 0x4 + 0 = 0x4. The result is 0x4FFFF.

If global_ulong is 0x4FFFF, then #1 changes
global_ulong+0 to 0x0000 and changes OF to 1. This time
#2 does have an effect: It changes global_ulong+2 to
0x5 and changes OF to 0. The result is 0x50000.

Notice that the compiler doesn't have to set any
"critical section" flags or call any mutexes for this.
It is impossible for an exception to happen after #1
is complete but before #2 is complete.

Now let's introduce hardware interrupts, or multiple
threads, or probably half a dozen other things I can't
think of at the moment, which interrupt the flow of
control in ways beyond the control of the compiler. These
things, naturally, are not included in the C++ language
specification, but as part of the real world they should
not be ignored.

Since these things can occur at ANY MOMENT, it is entirely
possible that the computer has already executed #1 but not
#2. If the value was 65536 or more, then we are not yet
finished. So what happens next?

In the case of an interrupt routine, there are well-defined
rules to handle the situation. The CPU automatically calls
a subroutine, which does whatever is needed to handle the
interrupt and then returns back to the point of the
interruption. The subroutine isn't supposed to rely on the
value of global_ulong or any other global data that is
modified by other routines. Once the main programs continues
execution #2 is executed and the eventual result is correct.

But what if the interrupt routine throws an exception?
Here we perform any neccesary stack operations and
eventually reach the nearest catch block. global_ulong
is left in it's potentially invalid state.

> ... it is possible to write exception-safe blocks of code
> (in some places auto_ptr suffice, in other places you may
> have to catch the exception, clean up, and rethrow.)

So why not wrap the statement in a try block?

    try {
        ++global_ulong;
    } catch (...) {
        ++global_ulong;
        throw;
    }

This is clearly wrong; some or all of global_ulong may be
incremented TWICE! (Consider what happens if the interrupt
occurs immediately AFTER #2.)  How about:

    unsigned long temp = global_ulong + 1;
    try {
        global_ulong = temp;
    } catch (...) {
        global_ulong = temp;
        throw;
    }

This is getting closer, but what happens if ANOTHER exception
happens while we're in the catch() block? We could keep
getting more and more complicated, but this is far enough.
Compare the complexity of the code above to the original two
machine code instructions! But if exceptions can happen at
arbitrary points, then without this level of complex (and
BIG! and SLOW!) code, even simple statements can't guarantee
correct results.

Even if you decide to bite the bullet and write code this way
all through your program -- this may not be enough. Some
subtle built-in feature of your compiler, which you don't even
think about, may need to update some data somewhere and may
do so with code that requires more than one machine cycle.
You won't know it until you crash, and probably not even then,
because you won't be able to reproduce it.

(Imagine the "bug" above was in production code. It would only
break when the interrupt happened between #1 and #2, and even
then the result would be correct 65535 out of 65536 times...)

My conclusion is that David Abrahams is right: if you can
trigger an exception at "an arbitrary sequence point" (such as
in an interrupt routine), all bets are off.

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Dave Abrahams" <abrahams@motu.com>
Date: 1999/02/10
Raw View
Petter Urkedal wrote in message <36C0167A.2D7C7660@matfys.lth.se>...
>
>Dave Abrahams wrote:
>> Did you propose some way to declare that a particular function might
throw a
>> signal exception? If so, I must have missed that.
>
>Exactly.  My message was probably a bit minimal.  To declare a function
>as throwing a signal exception shall mean that such an exception may be
>_implicitely_ thrown on any sequence point within it.

I'm still waiting to hear your proposal as to how a function will be
"declared as throwing a signal exception".

>> What happens when the vector's destructor calls a function which is
declared
>> as throwing signal exceptions? Is signal polling enabled?
>
>But that is not any more problematic than when a destructor calls
>a function which throws whatever else.  In practice destructors
>tends to call destructors, and exceptions in destructors can be
>avoided.  I have not encountered the problem yet, but I've seen that
>others are concerned about it.

There are other functions which must not throw, for example, operations on
STL iterators. It sounded like you were saying that they would be allowed to
throw a signal exception just so long as there was a catch block somewhere
up the call chain that would catch the exception.

Okay, you're not saying that. So with regard to catch blocks, you're only
proposing that polling could be automatically inserted by the compiler
between the braces. Any function which was "declared as throwing a signal
exception" would be subject to the insertion of automatic polling *within
its body only*.

>> What mechanism do you propose to use to declare that a function might
throw
>> signal exceptions? Remember that exception-specifications are not checked
>> across compilation units (and have other serious problems, anyway).
>
>Maybe I'm missing something here?  (What do you mean by `checked'?)
>To call a function, it must be declared, and `throw' declarations
>goes along with the declaration, so any compilation unit will see
>it.  We must require _explicit_ specification of signal exceptions.

Okay, we might have to extend exception-specifications to allow this:

    throw( signal_exception, ... )

Otherwise, you would have to specify every exception that a function could
throw if you wanted it to be able to throw signalling exceptions. There are
serious maintainance and robustness problems associated with trying to get
exception-specifications right. There are good reasons never to use them (or
only to use empty ones).

But even this extension scares me. It would be all too easy for users (me!)
to forget to write the ... part and cause unexpected program termination.

(My objection about checking across compilation units doesn't apply, now
that I understand your proposal better)

>> I think I understand your proposal, and as I understand it, it still
seems
>> unworkable. Even if you fixed the problems described above, given the
small
>> number of places where automatic polling might be inserted, you'd be
better
>> off doing it yourself.
>
>That depends on programming style.  A programmer which uses auto_ptr
>or memory managed pointers, and encapsulates access to other
>resources in classes may accept signal exceptions in most of his
>functions.  Anyway, function do throw various exceptions today, so I
>don't think the problem will be so radically more severe.


I'm not so sure. A valid implementation of auto_ptr (written by its
principal designer, Greg Colvin) transfers ownership by releasing the
pointer and putting a copy in an auto_ptr_ref, then constructing the new
auto_ptr using the auto_ptr_ref. As far as I can tell, there are several
sequence points along the way. If an implementation were allowed to insert
polling code in there, it would be easy to leak the pointer.

-Dave
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: stanley@West.Sun.COM (Stanley Friesen [Contractor])
Date: 1999/02/10
Raw View
In article <79q6m8$ruh$1@nnrp1.dejanews.com>,  <AllanW@my-dejanews.com> wrote:
>can only access 2 bytes at one time. On that computer, the
>compiler will do something comparable to this:
>
>    Add 1, global_ulong+0     ; #1
>    Add OF, global_ulong+2    ; #2
> ...
>Now let's introduce hardware interrupts, or multiple
>threads, or probably half a dozen other things I can't
>think of at the moment, which interrupt the flow of
>control in ways beyond the control of the compiler. ...
>
>But what if the interrupt routine throws an exception?
>Here we perform any neccesary stack operations and
>eventually reach the nearest catch block. global_ulong
>is left in it's potentially invalid state.

I think the proposal here dealt with this issue.

The proposal would be something like this:

1. Interrupt sets a per-thread "exception" flag, and does NOT
actually throw any excpetion.

2. At a *sequence* *point*  (which rules out the middle of an
increment operation), in a function *explicitly* declared to
throw the intr_exception, the compiler inserts a check of the
thread's exception flag, and causes a throw if it is set.


Now, I do have an objection to this system: it adds to the cost of simple
operations even when the exception is not thrown.  This comes from the
need to do an actual check at every relevent sequence point.  Now commas
and semicolons cause code to be generated!!
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1999/02/04
Raw View
Ziv Caspi wrote in message <36b6bd2c.61839410@news.netvision.net.il>...

>Assuring that when one thread "cancels" another all the destructors in
>the cancelled thread are called is equivalent to having inter-thread
>exceptions thrown. It seems that most people in the multi-thread
>community object to having async. exceptions thrown from one thread
>to another (I am not one of them, BTW).


I can understand why they object.  Asynchronous exceptions mean that
exceptions can be thrown at unexpected times:

  1) While currently executing a destructor.
  2) During transfer of ownership (such as auto_ptr assignment) where the
state of the objects is formally such that neither, or both, have ownership.
  3) While an exception is currently in progress.
  4) Before, during, or after a nothrow function call.

Obviously it is a pain for the programmer to have to call
pthread_setasynccancel before and after each of these operations, but IMO it
would be a bigger pain to have all threads pay the cost of having the
compiler generate these calls (and also the pthread_push and pop calls) for
threads that don't need them.

Perhaps it would be good to get some implementation experience (a pragma to
enable this behavior).
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Petter Urkedal <petter@matfys.lth.se>
Date: 1999/02/04
Raw View
A general solution to the thread-cancellation problem is to provide
exceptions upon signals, which thus builds on the already present
(ANSI C) awarance of signals. However, we would have to make some
restrictions, so that a function is not interrupted at an unexpected
point (and leaving allocated resources).

One possibility is to have a compiler-provided class `signal_raised'
which may be thrown at any _sequence point_ in

  * a try block which catches it or an exception derived from it

  * a function declared to throw signal_raised or an exception
    derived from it

At any other place, handling of the signal is delayed. For backward
compatibility and greater functionality, it is necessary to specify
(through a function) which signals should cause exceptions.

This would not interfere with efficiency of code at places where
signal_raised is not acknowledged. It _may_ reduce efficiency in
functions declared to throw signal excpetions, or in try blocks
catching them.  To lighten such loss of efficiency, it may be
unspecified how many sequence points is passed after a signal is
thrown until the exception is raised; although the delay should be
`resonable'.

 -petter.

--
[- http://matfys.lth.se/~petter/ -]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: zivc@peach-networks.com (Ziv Caspi)
Date: 1999/02/02
Raw View
On 30 Jan 99 23:09:26 GMT, mikem@ar.ar.com.au (Mike Mowbray) wrote:

[...]

Assuring that when one thread "cancels" another all the destructors in
the cancelled thread are called is equivalent to having inter-thread
exceptions thrown. It seems that most people in the multi-thread
community object to having async. exceptions thrown from one thread
to another (I am not one of them, BTW).

As a sidenote - Does POSIX (or pthread) distinguish "asking" a thread
to stop as opposed to forcibly terminatingn it?

Ziv

---------------------------------------------
Ziv Caspi
zivca@netvision.net.il
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Jonathan Biggar <jon@floorboard.com>
Date: 1999/02/02
Raw View
Ziv Caspi wrote:
>
> On 30 Jan 99 23:09:26 GMT, mikem@ar.ar.com.au (Mike Mowbray) wrote:
>
> [...]
>
> Assuring that when one thread "cancels" another all the destructors in
> the cancelled thread are called is equivalent to having inter-thread
> exceptions thrown. It seems that most people in the multi-thread
> community object to having async. exceptions thrown from one thread
> to another (I am not one of them, BTW).
>
> As a sidenote - Does POSIX (or pthread) distinguish "asking" a thread
> to stop as opposed to forcibly terminatingn it?

It doesn't really need to, since the POSIX pthread interface provides
all of the tools to build a cooperative thread cancelling mechanism.
Such a mechanism would require that the target thread poll for
cancellation attempts.  Once it detects a cancel request, all it needs
to do is throw a special exception that is used to unwind the stack.

--
Jon Biggar
Floorboard Software
jon@floorboard.com
jon@biggar.org
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: mikem@ar.ar.com.au (Mike Mowbray)
Date: 1999/01/30
Raw View
A line of discussion has occurred a couple of times in the past few months
in comp.programming.threads concerning the interaction of C++ and posix
thread cancellation. It's relevant to both C++ standardization and pthread
standardization, and especially relevant to platform-independent compiler
suppliers, as described below.

Consider some C++ code using on-stack objects with destructors. If an
exception is thrown, the destructors for these objects will be called as
the stack unwinds. Now suppose that the same code is being run in a
multi-threaded program that uses posix thread cancellation. To be specific,
suppose the code is running in thread#3 (say) and that thread#2 issues a
pthread_cancel() towards thread#3. As a user of C++ in a multi-threaded
environment, I'd like to have a guarantee that d'tors for any on-stack
destructors in thread#3 would be called as part of cancellation. The
absence of such guarantee could lead to resource leaks, of exactly the same
kind that originally motivated the C++ standard to guarantee such
dtor-calling when an exception is thrown.

Pthreads provides a mechanism for pushing/popping cleanup handlers (which
can specify user-functions to call when a thread is cancelled). However,
this is most inconvenient for C++ programming, because it would mean we'd
have to insert calls to pthread_cleanup_push() at (probably multiple)
points within our constructors, and calls to pthread_cleanup_pop() at
(multiple) points in our destructors. Worse, if we wish to use instances of
classes for which source code is not available, then even if this tedious
approach cannot be used.

The absence in the C++ standard of a guarantee that on-stack dtors will be
called in response to thread cancellation means that C++ in its current
form is not a "general purpose" programming language for producing robust
systems in a multithreaded environment. Although one can imagine ways of
using C++ and pthreads together which avoid this problem (such as not
cancelling threads) these represent a significant restriction on the styles
of C++ programming open to a developer of multithreaded systems.

One might even go further and assert that the same considerations should
apply when calling pthread_exit(), or when a thread is removed by the
system in response to some asynchronous event such as a signal. If a
*process* ends, the operating system guarantees to reclaim any system-wide
resources that it held, and the absence of such a guarantee would be
laughable. It seems logical, then, that in the case of a thread ending,
process resources owned by the thread should also be reclaimed
automatically. Failure to provide such a guarantee forces a thread
programmer to have global knowledge about the entire system, which is
highly error-prone at best, and impossible at worst.

Currently, the C++ standard does not address multi-threaded programming and
therefore we cannot make any (reliable) assumptions about dtor-calling in
relation to thread cancellation. Various vendors who supply both the
pthread library and a C++ compiler have already addressed this problem in
their implementations, and pthread_cancel() is integrated with the
stack-unwind code used in exception-handling. The C++ compilers and pthread
implementations from Sun and Compaq/DEC have this behaviour and (I'm told)
it's also done under IRIX. (I don't about other platforms. Readers with
access to other platforms may wish to try out the program I've appended
below which will reveal whether your compiler/platform combination is as a
"good citizen" in both the C++ and pthreads worlds, by which I mean that
dtors of on-stack objects get called when a thread is cancelled. Please
post your results if you do try it.)

Unfortunately, the happy situation of proper dtor-threadcancellation
interworking does not exist in general if your compiler supplier is
different from your platform supplier. E.g: g++ (egcs1.1b) fails to call
dtors of on-stack objects when a thread is cancelled. The current state of
affairs thus works against competition in the compiler industry, as well as
restricting the utility of the C++ standard within the opensource
community. A wide range of users is therefore potentially disadvantaged by
the current situation.

One purpose of this posting is to draw the attention of the C++ standards
community to this problem, in the hope that it may be addressed in some
future revision of the standard(s).

A second purpose, of more immediate urgency, is to urge
platform-independent compiler suppliers to take a close look at this
problem in relation to their own C++ compiler in a multi-threaded
environment. This posting is therefore also an appeal to
platform-independent compiler suppliers to address this problem as soon as
reasonably possible, because it harms the utility of C++ in a
multi-threaded environment.

--
Mike Mowbray            Internet: mikem@ar.com.au


//---------------------------------------------------------------------------
// The following C++ program examines stack unwinding behaviour for
// pthread cancellation. It will print out an error/ok indication, as well
// some progress messages.
//
// Don't forget to link it with "-lpthread" (depending on your platform,
// that is).
//
// Author:  Mike Mowbray.       mikem@ar.com.au
//---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
//----------------------------------------------------------------------------

class   Foobar
        // A class that does nothing except keep an instance count and
        // announces when it's being constructed/destructed.
{
        static int              the_instance_count;
        static pthread_mutex_t  the_instcount_mtx;

    public:
                                Foobar();
                                ~Foobar();

        static int              instcount()  { return the_instance_count; }
};
//----------------------------------------------------------------------------

int              Foobar::the_instance_count = 0;
pthread_mutex_t  Foobar::the_instcount_mtx  = PTHREAD_MUTEX_INITIALIZER;
//----------------------------------------------------------------------------

Foobar::Foobar()
{
    printf("thr#%u: creating Foobar.\n", pthread_self());
    fflush(stdout);

    {
        pthread_mutex_lock(&the_instcount_mtx);
        ++the_instance_count;
        pthread_mutex_unlock(&the_instcount_mtx);
    }
}
//----------------------------------------------------------------------------

Foobar::~Foobar()
{
    printf("thr#%u: destroying Foobar.\n", pthread_self());
    fflush(stdout);

    {
        pthread_mutex_lock(&the_instcount_mtx);
        --the_instance_count;
        pthread_mutex_unlock(&the_instcount_mtx);
    }
}
//----------------------------------------------------------------------------

static void  sleep_for_a_while()
{
    pthread_t  curr_thread = pthread_self();

    printf("thr#%u: sleeping...\n", curr_thread);
    fflush(stdout);
    sleep(10);

    // Normally, we shouldn't reach here, because parent thread will cancel
    // us while we're in the sleep() above. If the following lines appear
    // in the output, something is wrong.

    printf("thr#%u: awake...\n", curr_thread);
    fflush(stdout);

    printf("thr#%u: exiting within <sleep_for_a_while()>...\n",curr_thread);
    fflush(stdout);

    int status = 0;
    pthread_exit(&status);
}
//----------------------------------------------------------------------------

static void*  run_foobar_thread(void*)
{
    pthread_t  this_thread = pthread_self();

    Foobar     f;

    sleep_for_a_while();

    printf("thr#%u: back in <run_foobar_thread()>...\n", this_thread);
    fflush(stdout);

    return 0;
}
//----------------------------------------------------------------------------

main()
{
    pthread_t  other_thread = 0;
    int const  t = pthread_create(&other_thread, NULL, run_foobar_thread, 0);

    if (t != 0)
    {
        int const  e = errno;
        printf("pthread_create() returned %d. %s\n", t, strerror(e));
        fflush(stdout);
        return 1;
    }

    sleep(2);

    printf("Main thread cancelling thr#%u\n", other_thread);
    fflush(stdout);

    int err = pthread_cancel(other_thread);
    if (err != 0)
    {
        printf("pthread_cancel() : %s\n", strerror(err));
        fflush(stdout);
    }

    void*  status = 0;
    pthread_join(other_thread, &status);
    printf("Main thread join'ed with thr#%u\n", other_thread);
    fflush(stdout);

    int const  foobar_instcount = Foobar::instcount();

    if (foobar_instcount == 0)
        printf("OK. foobar_instcount = 0\n");
    else
        printf("ERROR! foobar_instcount=%d (expected 0)\n", foobar_instcount);

    fflush(stdout);

    return  (foobar_instcount==0 ? 0 : 1);
}
//----------------------------------------------------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]