This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [patch] Simplify allocator use
- From: Jonathan Wakely <jwakely at redhat dot com>
- To: libstdc++ at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Cc: François Dumont <frs dot dumont at gmail dot com>
- Date: Thu, 26 Jun 2014 12:31:10 +0100
- Subject: Re: [patch] Simplify allocator use
- Authentication-results: sourceware.org; auth=none
- References: <20140625205624 dot GD2711 at redhat dot com>
On 25/06/14 21:56 +0100, Jonathan Wakely wrote:
The other adds an RAII type to help manage pointers obtained from
allocators. The new type means I can remove several ugly try-catch
blocks that are all very similar in structure and have been bothering
me for some time. The new type also makes it trivial to support
allocators with fancy pointers, fixing long-standing (but not very
important) bugs in std::promise and std::shared_ptr.
This patch applies the __allocated_ptr type to hashtable_policy.h to
remove most explicit deallocation (yay!) The buckets are still
allocated and deallocated manually, because __allocated_ptr only works
for allocations of single objects, not arrays.
As well as __allocated_ptr this change relies on two things:
1) the node type has a trivial destructor, so we don't actually need
to call it, we can just reuse or release its storage.
(See 3.8 [basic.life] p1)
2) allocator_traits::construct and allocator_traits::destroy can be
used with an allocator that has a different value_type, so we don't
need to create a rebound copy to destroy every element, we can just
use the node-allocator.
(See http://cplusplus.github.io/LWG/lwg-active.html#2218 which is
Open, but I've discussed the issue with Howard, Pablo and others,
and I think libc++ already relies on this assumption).
François, could you check it, and let me know if you see anything
wrong or have any comments?
commit d2fd02daab715c79c766bc0a476d1d01da1fc305
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Thu Jun 26 12:28:56 2014 +0100
* include/bits/hashtable_policy.h (_ReuseOrAllocNode::operator()): Use
__allocated_ptr.
(_Hashtable_alloc::_M_allocate_node): Likewise.
(_Hashtable_alloc::_M_deallocate_node): Likewise.
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index 606fbab..ed6b2d7 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -31,6 +31,8 @@
#ifndef _HASHTABLE_POLICY_H
#define _HASHTABLE_POLICY_H 1
+#include <bits/allocated_ptr.h>
+
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -137,20 +139,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__node_type* __node = _M_nodes;
_M_nodes = _M_nodes->_M_next();
__node->_M_nxt = nullptr;
- __value_alloc_type __a(_M_h._M_node_allocator());
- __value_alloc_traits::destroy(__a, __node->_M_valptr());
- __try
- {
- __value_alloc_traits::construct(__a, __node->_M_valptr(),
- std::forward<_Arg>(__arg));
- }
- __catch(...)
- {
- __node->~__node_type();
- __node_alloc_traits::deallocate(_M_h._M_node_allocator(),
- __node, 1);
- __throw_exception_again;
- }
+ auto& __a = _M_h._M_node_allocator();
+ __node_alloc_traits::destroy(__a, __node->_M_valptr());
+ __allocated_ptr<_NodeAlloc> __guard{__a, __node};
+ __node_alloc_traits::construct(__a, __node->_M_valptr(),
+ std::forward<_Arg>(__arg));
+ __guard = nullptr;
return __node;
}
return _M_h._M_allocate_node(std::forward<_Arg>(__arg));
@@ -1947,33 +1941,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typename _Hashtable_alloc<_NodeAlloc>::__node_type*
_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&&... __args)
{
- auto __nptr = __node_alloc_traits::allocate(_M_node_allocator(), 1);
- __node_type* __n = std::__addressof(*__nptr);
- __try
- {
- __value_alloc_type __a(_M_node_allocator());
- ::new ((void*)__n) __node_type;
- __value_alloc_traits::construct(__a, __n->_M_valptr(),
- std::forward<_Args>(__args)...);
- return __n;
- }
- __catch(...)
- {
- __node_alloc_traits::deallocate(_M_node_allocator(), __nptr, 1);
- __throw_exception_again;
- }
+ auto& __a = _M_node_allocator();
+ auto __guard = std::__allocate_guarded(__a);
+ __node_type* __n = __guard.get();
+ ::new ((void*)__n) __node_type;
+ __node_alloc_traits::construct(__a, __n->_M_valptr(),
+ std::forward<_Args>(__args)...);
+ __guard = nullptr;
+ return __n;
}
template<typename _NodeAlloc>
void
_Hashtable_alloc<_NodeAlloc>::_M_deallocate_node(__node_type* __n)
{
- typedef typename __node_alloc_traits::pointer _Ptr;
- auto __ptr = std::pointer_traits<_Ptr>::pointer_to(*__n);
- __value_alloc_type __a(_M_node_allocator());
- __value_alloc_traits::destroy(__a, __n->_M_valptr());
- __n->~__node_type();
- __node_alloc_traits::deallocate(_M_node_allocator(), __ptr, 1);
+ static_assert(std::is_trivially_destructible<__node_type>::value,
+ "Nodes must not require non-trivial destruction");
+ auto& __alloc = _M_node_allocator();
+ __allocated_ptr<__node_alloc_type> __guard{__alloc, __n};
+ __node_alloc_traits::destroy(__alloc, __n->_M_valptr());
}
template<typename _NodeAlloc>