Re: [c++-pthreads] Restating the Jason model
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [c++-pthreads] Restating the Jason model



Wil Evers wrote:
[...]
> The situation is actually a bit more complicated: many container
> implementations, including the ones in the C++ standard library, assume
> that the destructors of their elements do not throw.   Most likely, an
> exception escaping from one element's destructor will cause the
> remaining elements to be abandoned (and not destroyed at all).

Here's sort of solution:

  template<class _FwdIt, class _Tval> inline
  void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
          _Nonscalar_ptr_iterator_tag)
  {       // copy _Val throughout raw [_First, _Last), arbitrary type
    _FwdIt _Next = _First;
    try {
      for (; _First != _Last; ++_First)
        _Construct(&*_First, _Val);
    }
    /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
    /* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
    /* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */
    action_on_propagation_of(...) {  
      for (; _Next != _First; ++_Next)
        _Destroy(&*_Next);
    }
  }

The idea is that if a destructor here attempts to exit with an
exception then std::unexpected() will be invoked at throw point. In a 
way, using hypothetical "no_throw {}" (in addition to hypothetical
"action_on_propagation_of()") it is equivalent to

  template<class _FwdIt, class _Tval> inline
  void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
          _Nonscalar_ptr_iterator_tag)
  {       // copy _Val throughout raw [_First, _Last), arbitrary type
    _FwdIt _Next = _First;
    try {
      for (; _First != _Last; ++_First)
        _Construct(&*_First, _Val);
    }
    /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
    /* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
    /* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */
    action_on_propagation_of(...) {  
      no_throw {
        for (; _Next != _First; ++_Next)
          _Destroy(&*_Next);
      }
    }
  }

presuming 2-phase (and a bit "extended") EH, of course.

> 
> > This is a somewhat persuasive case for disabling cancel during all
> > destructors, but I'm not sure it outweighs the overhead involved.  In the
> > model I proposed, only the EH runtime needs to know about cancellation.
> 
> As I tried to say before, my main objection to your model is that,
> depending on the thread's dynamic context, destructors will behave
> differently when hitting a cancellation point.  Sometimes, your model
> will protect us from cancellation exceptions, but at other times it
> won't, and I fear the confusion that might result from this.

Not that I really like Jason's model, but "confusion" does already 
exist in POSIX; I mean pthread_cleanup_pop(!0) "destructors".

> 
> I agree that the overhead of disabling cancellation in all destructors
> could become a problem.  

That's good.

>                         IMO, if we have sticky cancellation, the EH
> runtime doesn't need to be disable cancellation either.

But it has really nothing to do with stickiness. 

[...]
> The thing that changes because of cancellation is that some functions
> that didn't throw before will now be licensed to do so.  It seems to me
> that most of the participants on this mailing list accept that this will
> require changes to existing code.  That could include adding a try/catch
> block to destructor code.

If C++ would impose implicit ("by default") throw() specs on all 
destructors AND adopt 2-phase EH with "intelligent" cancel delivery, 
things like

object::~object() {
  fclose(/*...*/);
}

won't need any changes... and it won't impose any runtime overhead 
(on any modern PC-mapped EH impl) as long as you "run across" 
cancellation points disabled via throw specs WITHOUT pending cancel 
request.

regards,
alexander.