diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index c9cd62ab032..ba78ab8e914 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,16 @@ +2018-07-23 Jonathan Wakely + + PR libstdc++/70940 + * include/experimental/memory_resource + (__resource_adaptor_common::_AlignMgr::_M_unadjust): Add assertion. + (__resource_adaptor_common::__guaranteed_alignment): New helper to + give maximum alignment an allocator guarantees. Specialize for known + allocators using new and malloc. + (__resource_adaptor_imp::do_allocate): Use __guaranteed_alignment. + (__resource_adaptor_imp::do_deallocate): Likewise. + * testsuite/experimental/memory_resource/new_delete_resource.cc: + Check that new and delete are called with expected sizes. + 2018-07-20 Jonathan Wakely PR libstdc++/86595 diff --git a/libstdc++-v3/include/experimental/memory_resource b/libstdc++-v3/include/experimental/memory_resource index 1965fdcfe73..61273fc2c85 100644 --- a/libstdc++-v3/include/experimental/memory_resource +++ b/libstdc++-v3/include/experimental/memory_resource @@ -36,6 +36,13 @@ #include #include +namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + template class malloc_allocator; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace __gnu_cxx + namespace std { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -307,6 +314,10 @@ namespace pmr { __orig_ptr = __ptr - _S_read(__end); else // (__token_size == sizeof(char*)) __orig_ptr = _S_read(__end); + // The adjustment is always less than the requested alignment, + // so if that isn't true now then either the wrong size was passed + // to deallocate or the token was overwritten by a buffer overflow: + __glibcxx_assert(static_cast(__ptr - __orig_ptr) < _M_align); return __orig_ptr; } @@ -345,6 +356,23 @@ namespace pmr { return __val; } }; + + template + struct __guaranteed_alignment : std::integral_constant { }; + + template + struct __guaranteed_alignment<__gnu_cxx::new_allocator<_Tp>> + : std::alignment_of::type { }; + + template + struct __guaranteed_alignment<__gnu_cxx::malloc_allocator<_Tp>> + : std::alignment_of::type { }; + +#if _GLIBCXX_USE_ALLOCATOR_NEW + template + struct __guaranteed_alignment> + : std::alignment_of::type { }; +#endif }; // 8.7.1 __resource_adaptor_imp @@ -392,7 +420,7 @@ namespace pmr { virtual void* do_allocate(size_t __bytes, size_t __alignment) override { - if (__alignment == 1) + if (__alignment <= __guaranteed_alignment<_Alloc>::value) return _M_alloc.allocate(__bytes); const _AlignMgr __mgr(__bytes, __alignment); @@ -407,7 +435,7 @@ namespace pmr { override { auto __ptr = static_cast(__p); - if (__alignment == 1) + if (__alignment <= __guaranteed_alignment<_Alloc>::value) { _M_alloc.deallocate(__ptr, __bytes); return; diff --git a/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc b/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc index 692e520bf9a..a7c4b378b6f 100644 --- a/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc +++ b/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc @@ -18,16 +18,21 @@ // { dg-do run { target c++14 } } #include +#include #include bool new_called = false; bool delete_called = false; +std::size_t bytes_allocated = 0; void* operator new(std::size_t n) { new_called = true; if (void* p = malloc(n)) + { + bytes_allocated += n; return p; + } throw std::bad_alloc(); } @@ -35,13 +40,17 @@ void operator delete(void* p) { delete_called = true; std::free(p); + bytes_allocated = 0; // assume everything getting deleted } -void operator delete(void* p, std::size_t) +void operator delete(void* p, std::size_t n) { - ::operator delete(p); + delete_called = true; + std::free(p); + bytes_allocated -= n; } + template bool aligned(void* p) { @@ -92,36 +101,60 @@ test02() void test03() + { using std::max_align_t; using std::size_t; void* p = nullptr; + bytes_allocated = 0; + memory_resource* r1 = new_delete_resource(); p = r1->allocate(1); + VERIFY( bytes_allocated == 1 ); VERIFY( aligned(p) ); r1->deallocate(p, 1); - p = r1->allocate(1, alignof(short)); - VERIFY( aligned(p) ); - r1->deallocate(p, 1, alignof(short)); - p = r1->allocate(1, alignof(long)); - VERIFY( aligned(p) ); - r1->deallocate(p, 1, alignof(long)); - constexpr size_t big_al = alignof(max_align_t) * 8; - p = r1->allocate(1, big_al); - VERIFY( aligned(p) ); - r1->deallocate(p, 1, big_al); + VERIFY( bytes_allocated == 0 ); - // Test extended alignments - p = r1->allocate(1024, al6); + p = r1->allocate(2, alignof(char)); + VERIFY( bytes_allocated == 2 ); + VERIFY( aligned(p) ); + r1->deallocate(p, 2); + VERIFY( bytes_allocated == 0 ); + + p = r1->allocate(3, alignof(short)); + VERIFY( bytes_allocated == 3 ); + VERIFY( aligned(p) ); + r1->deallocate(p, 3, alignof(short)); + VERIFY( bytes_allocated == 0 ); + + p = r1->allocate(4, alignof(long)); + VERIFY( bytes_allocated == 4 ); + VERIFY( aligned(p) ); + r1->deallocate(p, 4, alignof(long)); + VERIFY( bytes_allocated == 0 ); + + // Test extended aligments: + p = r1->allocate(777, al6); + VERIFY( bytes_allocated >= 777 ); + VERIFY( bytes_allocated < (777 + al6 + 8) ); // reasonable upper bound VERIFY( aligned(p) ); - r1->deallocate(p, 1024, al6); - p = r1->allocate(1024, al12); + r1->deallocate(p, 777, al6); + VERIFY( bytes_allocated == 0 ); + + p = r1->allocate(888, al12); + VERIFY( bytes_allocated >= 888 ); + VERIFY( bytes_allocated < (888 + al12 + 8) ); // reasonable upper bound VERIFY( aligned(p) ); - r1->deallocate(p, 1024, al12); - p = r1->allocate(1024, al18); + r1->deallocate(p, 888, al12); + VERIFY( bytes_allocated == 0 ); + + p = r1->allocate(999, al18); + VERIFY( bytes_allocated >= 999 ); + VERIFY( bytes_allocated < (999 + al18 + 8) ); // reasonable upper bound VERIFY( aligned(p) ); - r1->deallocate(p, 1024, al18); + r1->deallocate(p, 999, al18); + VERIFY( bytes_allocated == 0 ); } int main()