[PATCH] libstdc++: Use scope guard for deallocating nodes in deque.
Tomasz Kaminski
tkaminsk@redhat.com
Fri May 9 15:48:50 GMT 2025
On Fri, May 9, 2025 at 5:25 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Fri, 9 May 2025 at 16:13, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
> >
> >
> >
> > On Thu, May 8, 2025 at 7:46 PM Jonathan Wakely <jwakely@redhat.com>
> wrote:
> >>
> >> On Fri, 18 Apr 2025 at 10:03, Tomasz Kamiński <tkaminsk@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);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250509/8fb2c5f2/attachment-0001.htm>
More information about the Libstdc++
mailing list