diff --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst --- a/libcxx/docs/Status/Cxx20.rst +++ b/libcxx/docs/Status/Cxx20.rst @@ -40,7 +40,7 @@ .. note:: - .. [#note-P0600] P0600: The missing bits in P0600 are in |sect|\ [mem.res.class] and |sect|\ [mem.poly.allocator.class]. + .. [#note-P0600] P0600: The missing bits in P0600 are in |sect|\ [mem.poly.allocator.class]. .. [#note-P0645] P0645: The paper is implemented but still marked as an incomplete feature (the feature-test macro is not set and the libary is only available when built with ``-fexperimental-library``). Not yet implemented LWG-issues will cause API and ABI breakage. diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -400,6 +400,7 @@ __memory/unique_ptr.h __memory/uses_allocator.h __memory/voidify.h + __memory_resource/memory_resource.h __mutex_base __node_handle __numeric/accumulate.h @@ -751,6 +752,7 @@ map math.h memory + memory_resource mutex new numbers diff --git a/libcxx/include/__memory_resource/memory_resource.h b/libcxx/include/__memory_resource/memory_resource.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__memory_resource/memory_resource.h @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___MEMORY_RESOURCE_MEMORY_RESOURCE_H +#define _LIBCPP___MEMORY_RESOURCE_MEMORY_RESOURCE_H + +#include <__config> +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 17 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace pmr { +class _LIBCPP_TYPE_VIS memory_resource { + static constexpr auto __max_align = alignof(max_align_t); + +public: + memory_resource() = default; + memory_resource(const memory_resource&) = default; + _LIBCPP_FUNC_VIS virtual ~memory_resource(); + + [[using __gnu__: __returns_nonnull__, + __alloc_size__(2), + __alloc_align__(3)]] [[nodiscard]] _LIBCPP_HIDE_FROM_ABI void* + allocate(size_t __bytes, size_t __alignment = __max_align) { + _LIBCPP_ASSERT(std::popcount(__alignment) == 1, "alignment has to be a power of two"); + return ::operator new(__bytes, do_allocate(__bytes, __alignment)); + } + + [[__gnu__::__nonnull__]] _LIBCPP_HIDE_FROM_ABI void + deallocate(void* __ptr, size_t __bytes, size_t __alignment = __max_align) { + return do_deallocate(__ptr, __bytes, __alignment); + } + + _LIBCPP_HIDE_FROM_ABI bool is_equal(const memory_resource& __other) const noexcept { return do_is_equal(__other); } + +private: + virtual void* do_allocate(size_t __bytes, size_t __alignment) = 0; + virtual void do_deallocate(void* __ptr, size_t __bytes, size_t __alignment) = 0; + + virtual bool do_is_equal(const memory_resource& __other) const noexcept = 0; +}; + +_LIBCPP_HIDE_FROM_ABI inline bool operator==(const memory_resource& __lhs, const memory_resource& __rhs) { + return &__lhs == &__rhs || __lhs.is_equal(__rhs); +} + +# if _LIBCPP_STD_VER == 17 +_LIBCPP_HIDE_FROM_ABI inline bool operator!=(const memory_resource& __lhs, const memory_resource& __rhs) { + return !(__lhs == __rhs); +} +# endif + +} // namespace pmr + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 17 + +#endif // _LIBCPP___MEMORY_RESOURCE_MEMORY_RESOURCE_H diff --git a/libcxx/include/memory_resource b/libcxx/include/memory_resource new file mode 100644 --- /dev/null +++ b/libcxx/include/memory_resource @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// namespace std::pmr { +// class memory_resource { +// public: +// memory_resource() = default; +// memory_resource(const memory_resource&) = default; +// virtual ~memory_resource(); +// +// memory_resource& operator=(const memory_resource&) = default; +// [[nodiscard]] void* allocate(size_t bytes, size_t alignment = max_align); +// void deallocate(void* p, size_t bytes, size_t alignment = max_align); +// +// private: +// virtual void* do_allocate(size_t bytes, size_t alignment) = 0; +// virtual void do_deallocate(void* p, size_t bytes, size_t alignment) = 0; +// +// virtual bool do_is_equal(const memory_resource& other) const noexcept = 0; +// }; +// } + +#ifndef _LIBCPP_MEMORY_RESOURCE +#define _LIBCPP_MEMORY_RESOURCE + +#include <__config> +#include <__memory_resource/memory_resource.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#endif // _LIBCPP_MEMORY_RESOURCE diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -31,6 +31,7 @@ include/to_chars_floating_point.h legacy_pointer_safety.cpp memory.cpp + memory_resource.cpp mutex.cpp mutex_destructor.cpp new.cpp diff --git a/libcxx/src/memory_resource.cpp b/libcxx/src/memory_resource.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/memory_resource.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace pmr { +_LIBCPP_FUNC_VIS memory_resource::~memory_resource() = default; +} // namespace pmr + +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/mem/mem.res/memory_resource.pass.cpp b/libcxx/test/std/mem/mem.res/memory_resource.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/mem/mem.res/memory_resource.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include + +int a; + +bool allocate_has_been_called = false; +bool deallocate_has_been_called = false; +bool is_equal_has_been_called = false; + +class simple_resource : public std::pmr::memory_resource { +protected: + void* do_allocate(size_t bytes, size_t alignment) override { + assert(bytes == 7); + assert(alignment == alignof(max_align_t)); + assert(!allocate_has_been_called); + allocate_has_been_called = true; + return &a; + } + + void do_deallocate(void* ptr, size_t bytes, size_t alignment) override { + assert(ptr == &a); + assert(bytes == 7); + assert(alignment == alignof(max_align_t)); + assert(!deallocate_has_been_called); + deallocate_has_been_called = true; + } + + bool do_is_equal(const std::pmr::memory_resource&) const noexcept override { + assert(!is_equal_has_been_called); + is_equal_has_been_called = true; + return false; + } +}; + +class equal_resource : public std::pmr::memory_resource { +public: + bool ret_equal = false; + +protected: + void* do_allocate(size_t, size_t) override { assert(false); } + + void do_deallocate(void*, size_t, size_t) override { assert(false); } + + bool do_is_equal(const std::pmr::memory_resource&) const noexcept override { return ret_equal; } +}; + +class throwing_resource : public std::pmr::memory_resource { +protected: + void* do_allocate(size_t, size_t) override { throw std::bad_alloc(); } + + void do_deallocate(void*, size_t, size_t) override { assert(false); } + + bool do_is_equal(const std::pmr::memory_resource&) const noexcept override { assert(false); } +}; + +static_assert(std::is_abstract_v); + +int main(int, char**) { + { + simple_resource r{}; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + void* ret = r.allocate(7); + assert(ret == &a); + assert(allocate_has_been_called); + r.deallocate(ret, 7); + assert(deallocate_has_been_called); + assert(!r.is_equal(r)); + assert(is_equal_has_been_called); + } + + { + equal_resource r1{}; + equal_resource r2{}; + assert(r1 == r1); + assert(!r1.is_equal(r1)); + assert(r1 != r2); + assert(!(r1 == r2)); + r1.ret_equal = true; + r2.ret_equal = true; + assert(r1 == r1); + assert(r1.is_equal(r1)); + assert(r1 == r2); + assert(!(r1 != r2)); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + { + throwing_resource r{}; + try { + (void)r.allocate(0); + assert(false); + } catch (const std::bad_alloc&) { + } + } +#endif + + return 0; +}