Commit 852a971c by Jonathan Wakely Committed by Jonathan Wakely

Implement std::pmr::unsynchronized_pool_resource

	Implement std::pmr::unsynchronized_pool_resource
	* config/abi/pre/gnu.ver: Add new symbols.
	* include/std/memory_resource (std::pmr::__pool_resource): New class.
	(std::pmr::unsynchronized_pool_resource): New class.
	* src/c++17/Makefile.am: Add -fimplicit-templates to flags for
	memory_resource.cc
	* src/c++17/Makefile.in: Regenerate.
	* src/c++17/memory_resource.cc (bitset, chunk, big_block): New
	internal classes.
	(__pool_resource::_Pool): Define new class.
	(munge_options, pool_index, select_num_pools): New internal functions.
	(__pool_resource::__pool_resource, __pool_resource::~__pool_resource)
	(__pool_resource::allocate, __pool_resource::deallocate)
	(__pool_resource::_M_alloc_pools): Define member functions.
	(unsynchronized_pool_resource::unsynchronized_pool_resource)
	(unsynchronized_pool_resource::~unsynchronized_pool_resource)
	(unsynchronized_pool_resource::release)
	(unsynchronized_pool_resource::_M_find_pool)
	(unsynchronized_pool_resource::do_allocate)
	(unsynchronized_pool_resource::do_deallocate): Define member
	functions.
	* testsuite/20_util/unsynchronized_pool_resource/allocate.cc: New
	test.
	* testsuite/20_util/unsynchronized_pool_resource/is_equal.cc: New
	test.
	* testsuite/20_util/unsynchronized_pool_resource/options.cc: New
	test.
	* testsuite/20_util/unsynchronized_pool_resource/release.cc: New
	test.

From-SVN: r265853
parent dd1501c5
2018-11-06 Jonathan Wakely <jwakely@redhat.com>
Implement std::pmr::unsynchronized_pool_resource
* config/abi/pre/gnu.ver: Add new symbols.
* include/std/memory_resource (std::pmr::__pool_resource): New class.
(std::pmr::unsynchronized_pool_resource): New class.
* src/c++17/Makefile.am: Add -fimplicit-templates to flags for
memory_resource.cc
* src/c++17/Makefile.in: Regenerate.
* src/c++17/memory_resource.cc (bitset, chunk, big_block): New
internal classes.
(__pool_resource::_Pool): Define new class.
(munge_options, pool_index, select_num_pools): New internal functions.
(__pool_resource::__pool_resource, __pool_resource::~__pool_resource)
(__pool_resource::allocate, __pool_resource::deallocate)
(__pool_resource::_M_alloc_pools): Define member functions.
(unsynchronized_pool_resource::unsynchronized_pool_resource)
(unsynchronized_pool_resource::~unsynchronized_pool_resource)
(unsynchronized_pool_resource::release)
(unsynchronized_pool_resource::_M_find_pool)
(unsynchronized_pool_resource::do_allocate)
(unsynchronized_pool_resource::do_deallocate): Define member
functions.
* testsuite/20_util/unsynchronized_pool_resource/allocate.cc: New
test.
* testsuite/20_util/unsynchronized_pool_resource/is_equal.cc: New
test.
* testsuite/20_util/unsynchronized_pool_resource/options.cc: New
test.
* testsuite/20_util/unsynchronized_pool_resource/release.cc: New
test.
2018-11-06 John Bytheway <jbytheway@gmail.com> 2018-11-06 John Bytheway <jbytheway@gmail.com>
PR libstdc++/87872 PR libstdc++/87872
......
...@@ -2055,6 +2055,15 @@ GLIBCXX_3.4.26 { ...@@ -2055,6 +2055,15 @@ GLIBCXX_3.4.26 {
_ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE4openEPKwSt13_Ios_Openmode; _ZNSt13basic_filebufI[cw]St11char_traitsI[cw]EE4openEPKwSt13_Ios_Openmode;
_ZN11__gnu_debug25_Safe_local_iterator_base16_M_attach_singleEPNS_19_Safe_sequence_baseEb; _ZN11__gnu_debug25_Safe_local_iterator_base16_M_attach_singleEPNS_19_Safe_sequence_baseEb;
# <memory_resource> members
_ZTINSt3pmr28unsynchronized_pool_resourceE;
_ZNSt3pmr28unsynchronized_pool_resourceC[12]ERKNS_12pool_optionsEPNS_15memory_resourceE;
_ZNSt3pmr28unsynchronized_pool_resourceD[12]Ev;
_ZNSt3pmr28unsynchronized_pool_resource7releaseEv;
_ZNSt3pmr28unsynchronized_pool_resource11do_allocateEmm;
_ZNSt3pmr28unsynchronized_pool_resource13do_deallocateEPvmm;
} GLIBCXX_3.4.25; } GLIBCXX_3.4.25;
# Symbols in the support library (libsupc++) have their own tag. # Symbols in the support library (libsupc++) have their own tag.
......
...@@ -33,9 +33,9 @@ ...@@ -33,9 +33,9 @@
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#include <bit> // __ceil2, __log2p1
#include <memory> // align, allocator_arg_t, __uses_alloc #include <memory> // align, allocator_arg_t, __uses_alloc
#include <utility> // pair, index_sequence #include <utility> // pair, index_sequence
#include <vector> // vector
#include <cstddef> // size_t, max_align_t #include <cstddef> // size_t, max_align_t
#include <debug/assertions.h> #include <debug/assertions.h>
...@@ -296,8 +296,107 @@ namespace pmr ...@@ -296,8 +296,107 @@ namespace pmr
size_t largest_required_pool_block = 0; size_t largest_required_pool_block = 0;
}; };
// TODO class synchronized_pool_resource; // Common implementation details for unsynchronized/synchronized pool resources.
// TODO class unsynchronized_pool_resource; class __pool_resource
{
friend class synchronized_pool_resource;
friend class unsynchronized_pool_resource;
__pool_resource(const pool_options& __opts, memory_resource* __upstream);
~__pool_resource();
__pool_resource(const __pool_resource&) = delete;
__pool_resource& operator=(const __pool_resource&) = delete;
// Allocate a large unpooled block.
void*
allocate(size_t __bytes, size_t __alignment);
// Deallocate a large unpooled block.
void
deallocate(void* __p, size_t __bytes, size_t __alignment);
// Deallocate unpooled memory.
void release() noexcept;
memory_resource* resource() const noexcept
{ return _M_unpooled.get_allocator().resource(); }
struct _Pool;
_Pool* _M_alloc_pools();
const pool_options _M_opts;
struct _BigBlock;
// Collection of blocks too big for any pool, sorted by address.
// This also stores the only copy of the upstream memory resource pointer.
pmr::vector<_BigBlock> _M_unpooled;
const int _M_npools;
};
// TODO class synchronized_pool_resource
/// A non-thread-safe memory resource that manages pools of fixed-size blocks.
class unsynchronized_pool_resource : public memory_resource
{
public:
[[__gnu__::__nonnull__]]
unsynchronized_pool_resource(const pool_options& __opts,
memory_resource* __upstream);
unsynchronized_pool_resource()
: unsynchronized_pool_resource(pool_options(), get_default_resource())
{ }
[[__gnu__::__nonnull__]]
explicit
unsynchronized_pool_resource(memory_resource* __upstream)
: unsynchronized_pool_resource(pool_options(), __upstream)
{ }
explicit
unsynchronized_pool_resource(const pool_options& __opts)
: unsynchronized_pool_resource(__opts, get_default_resource()) { }
unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
virtual ~unsynchronized_pool_resource();
unsynchronized_pool_resource&
operator=(const unsynchronized_pool_resource&) = delete;
void release();
[[__gnu__::__returns_nonnull__]]
memory_resource*
upstream_resource() const noexcept
{ return _M_impl.resource(); }
pool_options options() const noexcept { return _M_impl._M_opts; }
protected:
void*
do_allocate(size_t __bytes, size_t __alignment) override;
void
do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
bool
do_is_equal(const memory_resource& __other) const noexcept override
{ return this == &__other; }
private:
using _Pool = __pool_resource::_Pool;
auto _M_find_pool(size_t) noexcept;
__pool_resource _M_impl;
_Pool* _M_pools = nullptr;
};
class monotonic_buffer_resource : public memory_resource class monotonic_buffer_resource : public memory_resource
{ {
......
...@@ -63,6 +63,11 @@ AM_CXXFLAGS = \ ...@@ -63,6 +63,11 @@ AM_CXXFLAGS = \
AM_MAKEFLAGS = \ AM_MAKEFLAGS = \
"gxx_include_dir=$(gxx_include_dir)" "gxx_include_dir=$(gxx_include_dir)"
memory_resource.lo: memory_resource.cc
$(LTCXXCOMPILE) -fimplicit-templates -c $< -o $@
memory_resource.o: memory_resource.cc
$(CXXCOMPILE) -fimplicit-templates -c $< -o $@
# Libtool notes # Libtool notes
# 1) In general, libtool expects an argument such as `--tag=CXX' when # 1) In general, libtool expects an argument such as `--tag=CXX' when
......
...@@ -731,6 +731,11 @@ uninstall-am: ...@@ -731,6 +731,11 @@ uninstall-am:
vpath % $(top_srcdir)/src/c++17 vpath % $(top_srcdir)/src/c++17
memory_resource.lo: memory_resource.cc
$(LTCXXCOMPILE) -fimplicit-templates -c $< -o $@
memory_resource.o: memory_resource.cc
$(CXXCOMPILE) -fimplicit-templates -c $< -o $@
# Tell versions [3.59,3.63) of GNU make to not export all variables. # Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded. # Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT: .NOEXPORT:
// Copyright (C) 2018 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++17" }
// { dg-do run { target c++17 } }
#include <memory_resource>
#include <cstring>
#include <testsuite_allocator.h>
#include <testsuite_hooks.h>
void
test01()
{
__gnu_test::memory_resource test_mr;
{
std::pmr::unsynchronized_pool_resource r(&test_mr);
void* p1 = r.allocate(1, 1);
VERIFY( p1 != nullptr );
auto n = test_mr.number_of_active_allocations();
VERIFY( n > 0 );
// Ensure memory region can be written to (without corrupting heap!)
std::memset(p1, 0xff, 1);
void* p2 = r.allocate(1, 1);
VERIFY( p2 != nullptr );
VERIFY( p2 != p1 );
VERIFY( test_mr.number_of_active_allocations() == n );
std::memset(p1, 0xff, 1);
r.deallocate(p1, 1, 1);
// Returning single blocks to the pool doesn't return them upstream:
VERIFY( test_mr.number_of_active_allocations() == n );
r.deallocate(p2, 1, 1);
VERIFY( test_mr.number_of_active_allocations() == n );
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
}
void
test02()
{
struct nullable_memory_resource : public std::pmr::memory_resource
{
void*
do_allocate(std::size_t bytes, std::size_t alignment) override
{ return upstream->allocate(bytes, alignment); }
void
do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override
{ upstream->deallocate(p, bytes, alignment); }
bool
do_is_equal(const memory_resource& r) const noexcept override
{ return &r == this; }
std::pmr::memory_resource* upstream = std::pmr::get_default_resource();
};
nullable_memory_resource test_mr;
std::pmr::unsynchronized_pool_resource r(&test_mr);
void* p1 = r.allocate(8, 1);
VERIFY( p1 != nullptr );
std::memset(p1, 0xff, 8);
test_mr.upstream = nullptr;
void* p2 = r.allocate(8, 1); //should not need to replenish
VERIFY( p2 != nullptr );
VERIFY( p2 != p1 );
std::memset(p1, 0xff, 8);
r.deallocate(p1, 8, 1); // should not use upstream
r.deallocate(p2, 8, 1); // should not use upstream
// Destructor will return memory upstream, so restore the upstream resource:
test_mr.upstream = std::pmr::get_default_resource();
}
void
test03()
{
__gnu_test::memory_resource test_mr;
{
std::pmr::unsynchronized_pool_resource r({10, 16}, &test_mr);
std::size_t largest_pool = r.options().largest_required_pool_block;
void* p1 = r.allocate(2 * largest_pool);
VERIFY( p1 != nullptr );
const std::size_t n = test_mr.number_of_active_allocations();
// Allocation of pools + allocation of pmr::vector + oversize allocation:
VERIFY( n >= 1 );
std::memset(p1, 0xff, 2 * largest_pool);
void* p2 = r.allocate(3 * largest_pool);
VERIFY( p2 != nullptr );
VERIFY( p2 != p1 );
VERIFY( test_mr.number_of_active_allocations() == n + 1 );
std::memset(p2, 0xff, 3 * largest_pool);
r.deallocate(p1, 2 * largest_pool);
VERIFY( test_mr.number_of_active_allocations() == n );
r.deallocate(p2, 3 * largest_pool);
VERIFY( test_mr.number_of_active_allocations() == n - 1 );
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
{
std::pmr::unsynchronized_pool_resource r({16, 16}, &test_mr);
(void) r.allocate(2);
(void) r.allocate(8);
(void) r.allocate(16);
(void) r.allocate(2);
(void) r.allocate(8);
(void) r.allocate(16);
(void) r.allocate(2 * r.options().largest_required_pool_block);
VERIFY( test_mr.number_of_active_allocations() != 0 );
// Destructor calls release()
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
}
void
test04()
{
std::pmr::unsynchronized_pool_resource r({256, 256});
// Check alignment
void* p1 = r.allocate(2, 64);
VERIFY( (std::uintptr_t)p1 % 64 == 0 );
void* p2 = r.allocate(2, 128);
VERIFY( (std::uintptr_t)p2 % 128 == 0 );
void* p3 = r.allocate(2, 256);
VERIFY( (std::uintptr_t)p3 % 256 == 0 );
const std::size_t largest_pool = r.options().largest_required_pool_block;
void* p4 = r.allocate(2 * largest_pool, 1024);
VERIFY( (std::uintptr_t)p4 % 1024 == 0 );
r.deallocate(p1, 2, 64);
r.deallocate(p2, 2, 128);
r.deallocate(p3, 2, 256);
r.deallocate(p4, 2 * largest_pool, 1024);
}
int
main()
{
test01();
test02();
test03();
test04();
}
// Copyright (C) 2018 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++17" }
// { dg-do run { target c++17 } }
#include <memory_resource>
#include <testsuite_hooks.h>
void
test01()
{
std::pmr::unsynchronized_pool_resource r1;
VERIFY( r1 == r1 );
std::pmr::unsynchronized_pool_resource r2;
VERIFY( r1 != r2 );
VERIFY( r2 != r1 );
}
int
main()
{
test01();
}
// Copyright (C) 2018 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++17" }
// { dg-do run { target c++17 } }
#include <memory_resource>
#include <testsuite_hooks.h>
void
test01()
{
std::pmr::unsynchronized_pool_resource r0;
const std::pmr::pool_options opts = r0.options();
VERIFY( opts.max_blocks_per_chunk != 0 );
VERIFY( opts.largest_required_pool_block != 0 );
std::pmr::unsynchronized_pool_resource r1(opts);
auto [max_blocks_per_chunk, largest_required_pool_block ] = r1.options();
VERIFY( max_blocks_per_chunk == opts.max_blocks_per_chunk );
VERIFY( largest_required_pool_block == opts.largest_required_pool_block );
}
int
main()
{
test01();
}
// Copyright (C) 2018 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++17" }
// { dg-do run { target c++17 } }
#include <memory_resource>
#include <testsuite_allocator.h>
#include <testsuite_hooks.h>
void
test01()
{
__gnu_test::memory_resource test_mr;
std::pmr::unsynchronized_pool_resource r(&test_mr);
r.release();
VERIFY( test_mr.number_of_active_allocations() == 0 );
r.release();
VERIFY( test_mr.number_of_active_allocations() == 0 );
(void) r.allocate(1);
VERIFY( test_mr.number_of_active_allocations() != 0 );
r.release();
VERIFY( test_mr.number_of_active_allocations() == 0 );
r.release();
VERIFY( test_mr.number_of_active_allocations() == 0 );
}
void
test02()
{
struct nullable_memory_resource : public std::pmr::memory_resource
{
void*
do_allocate(std::size_t bytes, std::size_t alignment) override
{ return upstream->allocate(bytes, alignment); }
void
do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override
{ upstream->deallocate(p, bytes, alignment); }
bool
do_is_equal(const memory_resource& r) const noexcept override
{ return &r == this; }
std::pmr::memory_resource* upstream = std::pmr::get_default_resource();
};
nullable_memory_resource test_mr;
std::pmr::unsynchronized_pool_resource r(&test_mr);
r.release();
test_mr.upstream = nullptr;
r.release(); // should not need to call anything through upstream pointer
}
void
test03()
{
__gnu_test::memory_resource test_mr;
{
std::pmr::unsynchronized_pool_resource r(&test_mr);
// Destructor calls release()
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
{
std::pmr::unsynchronized_pool_resource r(&test_mr);
(void) r.allocate(1);
VERIFY( test_mr.number_of_active_allocations() != 0 );
// Destructor calls release()
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
{
std::pmr::unsynchronized_pool_resource r({10, 16}, &test_mr);
(void) r.allocate(2 * r.options().largest_required_pool_block);
VERIFY( test_mr.number_of_active_allocations() != 0 );
// Destructor calls release()
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
{
std::pmr::unsynchronized_pool_resource r({16, 16}, &test_mr);
(void) r.allocate(2);
(void) r.allocate(8);
(void) r.allocate(16);
(void) r.allocate(2);
(void) r.allocate(8);
(void) r.allocate(16);
(void) r.allocate(2 * r.options().largest_required_pool_block);
VERIFY( test_mr.number_of_active_allocations() != 0 );
// Destructor calls release()
}
VERIFY( test_mr.number_of_active_allocations() == 0 );
}
int
main()
{
test01();
test02();
test03();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment