On Fri, May 9, 2025 at 5:25 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> On Fri, 9 May 2025 at 16:13, Tomasz Kaminski <tkami...@redhat.com> wrote:
> >
> >
> >
> > On Thu, May 8, 2025 at 7:46 PM Jonathan Wakely <jwak...@redhat.com>
> wrote:
> >>
> >> On Fri, 18 Apr 2025 at 10:03, Tomasz Kamiński <tkami...@redhat.com>
> wrote:
> >> >
> >> > This patch adds a _Guard_nodes scope guard nested to the _Deque_base,
> >> > that deallocates the range of nodes, and replaces __try/__catch block
> >> > with approparietly constructed guard object.
> >>
> >> "appropriately"
> >>
> >> >
> >> > libstdc++-v3/ChangeLog:
> >> >
> >> >         * include/bits/deque.tcc (_Deque_base<_Tp,
> _Alloc>::_Guard_nodes): Define.
> >>
> >> There's no need for the template argument list here, just
> >> "_Deque_base" is unambiguous (there's no partial or explicit
> >> specialization that could be disambiguated with template argument
> >> lists). And just "deque" below.
> >>
> >> >         (_Deque_base<_Tp, _Alloc>::_M_create_nodes): Moved defintion
> from stl_deque.h
> >> >         and replace __try/__catch with _Guard_nodes scope object.
> >> >         (deque<_Tp, _Alloc>::_M_fill_insert, deque<_Tp,
> _Alloc>::_M_default_append)
> >> >         (deque<_Tp, _Alloc>::_M_push_back_aux, deque<_Tp,
> _Alloc>::_M_push_front_aux)
> >> >         (deque<_Tp, _Alloc>::_M_range_prepend, deque<_Tp,
> _Alloc>::_M_range_append)
> >> >         (deque<_Tp, _Alloc>::_M_insert_aux): Replace __try/__catch
> with _Guard_nodes
> >> >         scope object.
> >> >         (deque<_Tp, _Alloc>::_M_new_elements_at_back)
> >> >         (deque<_Tp, _Alloc>::_M_new_elements_at_back): Use
> _M_create_nodes.
> >> >         * include/bits/stl_deque.h (_Deque_base<_Tp,
> _Alloc>::_Guard_nodes): Declare.
> >> >         (_Deque_base<_Tp, _Alloc)::_M_create_nodes): Move defintion
> to deque.tcc.
> >> >         (deque<_Tp, _Alloc>::_Guard_nodes): Add typedef, so name is
> found by lookup.
> >> > ---
> >> > Testing x86_64-linux, default test configuration passed.
> >> > OK for trunk?
> >> >
> >> >  libstdc++-v3/include/bits/deque.tcc   | 424
> ++++++++++++--------------
> >> >  libstdc++-v3/include/bits/stl_deque.h |  20 +-
> >> >  2 files changed, 196 insertions(+), 248 deletions(-)
> >> >
> >> > diff --git a/libstdc++-v3/include/bits/deque.tcc
> b/libstdc++-v3/include/bits/deque.tcc
> >> > index dabb6ec5365..b70eed69294 100644
> >> > --- a/libstdc++-v3/include/bits/deque.tcc
> >> > +++ b/libstdc++-v3/include/bits/deque.tcc
> >> > @@ -63,6 +63,40 @@ namespace std _GLIBCXX_VISIBILITY(default)
> >> >  _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >> >  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> >> >
> >> > +  template<typename _Tp, typename _Alloc>
> >> > +    struct
> >>
> >> No new line here, just "struct _Deque_base...".
> >>
> >> > +    _Deque_base<_Tp, _Alloc>::_Guard_nodes
> >> > +      {
> >> > +       _Guard_nodes(_Deque_base& __self,
> >> > +                    _Map_pointer __first, _Map_pointer __last)
> >> > +       : _M_self(__self), _M_first(__first), _M_last(__last)
> >> > +       { }
> >> > +
> >> > +       ~_Guard_nodes()
> >> > +       { _M_self._M_destroy_nodes(_M_first, _M_last); }
> >> > +
> >> > +       void _M_disarm()
> >> > +       { _M_first = _M_last; }
> >> > +
> >> > +       _Deque_base& _M_self;
> >> > +       _Map_pointer _M_first;
> >> > +       _Map_pointer _M_last;
> >> > +
> >> > +      private:
> >> > +       _Guard_nodes(_Guard_nodes const&);
> >> > +      };
> >> > +
> >> > +  template<typename _Tp, typename _Alloc>
> >> > +    void
> >> > +    _Deque_base<_Tp, _Alloc>::
> >> > +    _M_create_nodes(_Map_pointer __nstart, _Map_pointer __nfinish)
> >> > +    {
> >> > +      _Guard_nodes __guard(*this, __nstart, __nstart);
> >> > +      for (_Map_pointer& __cur = __guard._M_last; __cur < __nfinish;
> ++__cur)
> >> > +       *__cur = this->_M_allocate_node();
> >> > +      __guard._M_disarm();
> >> > +    }
> >> > +
> >> >  #if __cplusplus >= 201103L
> >> >    template <typename _Tp, typename _Alloc>
> >> >      void
> >> > @@ -310,35 +344,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> >> >        if (__pos._M_cur == this->_M_impl._M_start._M_cur)
> >> >         {
> >> >           iterator __new_start = _M_reserve_elements_at_front(__n);
> >> > -         __try
> >> > -           {
> >> > -             std::__uninitialized_fill_a(__new_start,
> this->_M_impl._M_start,
> >> > -                                         __x, _M_get_Tp_allocator());
> >> > -             this->_M_impl._M_start = __new_start;
> >> > -           }
> >> > -         __catch(...)
> >> > -           {
> >> > -             _M_destroy_nodes(__new_start._M_node,
> >> > -                              this->_M_impl._M_start._M_node);
> >> > -             __throw_exception_again;
> >> > -           }
> >> > +         _Guard_nodes __guard(*this, __new_start._M_node,
> >> > +                                     this->_M_impl._M_start._M_node);
> >> > +
> >> > +         std::__uninitialized_fill_a(__new_start,
> this->_M_impl._M_start,
> >> > +                                     __x, _M_get_Tp_allocator());
> >> > +         __guard._M_disarm();
> >> > +         this->_M_impl._M_start = __new_start;
> >> >         }
> >> >        else if (__pos._M_cur == this->_M_impl._M_finish._M_cur)
> >> >         {
> >> >           iterator __new_finish = _M_reserve_elements_at_back(__n);
> >> > -         __try
> >> > -           {
> >> > -             std::__uninitialized_fill_a(this->_M_impl._M_finish,
> >> > -                                         __new_finish, __x,
> >> > -                                         _M_get_Tp_allocator());
> >> > -             this->_M_impl._M_finish = __new_finish;
> >> > -           }
> >> > -         __catch(...)
> >> > -           {
> >> > -             _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1,
> >> > -                              __new_finish._M_node + 1);
> >> > -             __throw_exception_again;
> >> > -           }
> >> > +         _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node
> + 1,
> >> > +                                     __new_finish._M_node + 1);
> >> > +
> >> > +         std::__uninitialized_fill_a(this->_M_impl._M_finish,
> >> > +                                     __new_finish, __x,
> >> > +                                     _M_get_Tp_allocator());
> >> > +         __guard._M_disarm();
> >> > +         this->_M_impl._M_finish = __new_finish;
> >> >         }
> >> >        else
> >> >         _M_insert_aux(__pos, __n, __x);
> >> > @@ -350,23 +374,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> >> >      deque<_Tp, _Alloc>::
> >> >      _M_default_append(size_type __n)
> >> >      {
> >> > -      if (__n)
> >> > -       {
> >> > -         iterator __new_finish = _M_reserve_elements_at_back(__n);
> >> > -         __try
> >> > -           {
> >> > -             std::__uninitialized_default_a(this->_M_impl._M_finish,
> >> > -                                            __new_finish,
> >> > -                                            _M_get_Tp_allocator());
> >> > -             this->_M_impl._M_finish = __new_finish;
> >> > -           }
> >> > -         __catch(...)
> >> > -           {
> >> > -             _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1,
> >> > -                              __new_finish._M_node + 1);
> >> > -             __throw_exception_again;
> >> > -           }
> >> > -       }
> >> > +      if (!__n)
> >> > +       return;
> >> > +
> >> > +      iterator __new_finish = _M_reserve_elements_at_back(__n);
> >> > +      _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node +
> 1,
> >> > +                                 __new_finish._M_node + 1);
> >> > +
> >> > +      std::__uninitialized_default_a(this->_M_impl._M_finish,
> >> > +                                    __new_finish,
> >> > +                                    _M_get_Tp_allocator());
> >> > +      __guard._M_disarm();
> >> > +      this->_M_impl._M_finish = __new_finish;
> >> >      }
> >> >
> >> >    template <typename _Tp, typename _Alloc>
> >> > @@ -495,24 +514,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> >> >
> >> >         _M_reserve_map_at_back();
> >> >         *(this->_M_impl._M_finish._M_node + 1) =
> this->_M_allocate_node();
> >> > -       __try
> >> > -         {
> >> > +       _Guard_nodes __guard(*this, this->_M_impl._M_finish._M_node,
> >> > +                                   this->_M_impl._M_finish._M_node +
> 1);
> >> >  #if __cplusplus >= 201103L
> >> > -           _Alloc_traits::construct(this->_M_impl,
> >> > -                                    this->_M_impl._M_finish._M_cur,
> >> > -                                    std::forward<_Args>(__args)...);
> >> > +       _Alloc_traits::construct(this->_M_impl,
> >> > +                                this->_M_impl._M_finish._M_cur,
> >> > +                                std::forward<_Args>(__args)...);
> >> >  #else
> >> > -           this->_M_impl.construct(this->_M_impl._M_finish._M_cur,
> __t);
> >> > +       this->_M_impl.construct(this->_M_impl._M_finish._M_cur, __t);
> >> >  #endif
> >> > -
>  this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node
> >> > -                                               + 1);
> >> > -           this->_M_impl._M_finish._M_cur =
> this->_M_impl._M_finish._M_first;
> >> > -         }
> >> > -       __catch(...)
> >> > -         {
> >> > -           _M_deallocate_node(*(this->_M_impl._M_finish._M_node +
> 1));
> >>
> >> This deallocates _M_node+1 but the guard object guards
> >> [_M_node,_M_node+1). I think there's an off-by-one error here.
> >
> > Yes, indeed. I will also add a test that will detect this.
>
> Thanks. I think the rest of it looked OK, but I'll go over it again
> carefully.
>
> I did wonder about the "early" calls to _M_disarm(), do all the
> operations after the _M_disarm() calls guarantee not to throw?
>
> They are not early, they are placed before operation that "disarmed" the
deallocate in __catch. For example:
+             __guard._M_disarm();
+             this->_M_impl._M_finish = __new_finish;
+             _GLIBCXX_MOVE_BACKWARD3(__pos, __finish_n, __old_finish);
+             std::copy(__first, __last, __pos);
            }
-         __catch(...)
+         else
            {
// Setting this->_M_impl._M_finish to __new_finish made this range empty,
and disarmed.
-             _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1,
-                              __new_finish._M_node + 1);

Reply via email to