1// <experimental/memory_resource> -*- C++ -*- 2 3// Copyright (C) 2015-2020 Free Software Foundation, Inc. 4// 5// This file is part of the GNU ISO C++ Library. This library is free 6// software; you can redistribute it and/or modify it under the 7// terms of the GNU General Public License as published by the 8// Free Software Foundation; either version 3, or (at your option) 9// any later version. 10 11// This library is distributed in the hope that it will be useful, 12// but WITHOUT ANY WARRANTY; without even the implied warranty of 13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14// GNU General Public License for more details. 15 16// Under Section 7 of GPL version 3, you are granted additional 17// permissions described in the GCC Runtime Library Exception, version 18// 3.1, as published by the Free Software Foundation. 19 20// You should have received a copy of the GNU General Public License and 21// a copy of the GCC Runtime Library Exception along with this program; 22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23// <http://www.gnu.org/licenses/>. 24 25/** @file experimental/memory_resource 26 * This is a TS C++ Library header. 27 * @ingroup libfund-ts 28 */ 29 30#ifndef _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE 31#define _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE 1 32 33#pragma GCC system_header 34 35#if __cplusplus >= 201402L 36 37#include <memory> // align, uses_allocator, __uses_alloc 38#include <experimental/utility> // pair, experimental::erased_type 39#include <atomic> // atomic 40#include <new> // placement new 41#include <cstddef> // max_align_t 42#include <ext/new_allocator.h> 43#include <debug/assertions.h> 44 45/// @cond 46namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) 47{ 48_GLIBCXX_BEGIN_NAMESPACE_VERSION 49 template<typename _Tp> class malloc_allocator; 50_GLIBCXX_END_NAMESPACE_VERSION 51} // namespace __gnu_cxx 52/// @endcond 53 54namespace std { 55_GLIBCXX_BEGIN_NAMESPACE_VERSION 56 57namespace experimental { 58inline namespace fundamentals_v2 { 59namespace pmr { 60#define __cpp_lib_experimental_memory_resources 201402L 61 62 // Standard memory resources 63 64 // 8.5 Class memory_resource 65 class memory_resource; 66 67 // 8.6 Class template polymorphic_allocator 68 template<typename _Tp> 69 class polymorphic_allocator; 70 71 template<typename _Alloc, typename _Resource = memory_resource> 72 class __resource_adaptor_imp; 73 74 // 8.7 Alias template resource_adaptor 75 template<typename _Alloc> 76 using resource_adaptor = __resource_adaptor_imp< 77 typename allocator_traits<_Alloc>::template rebind_alloc<char>>; 78 79 // 8.8 Global memory resources 80 memory_resource* new_delete_resource() noexcept; 81 memory_resource* null_memory_resource() noexcept; 82 memory_resource* get_default_resource() noexcept; 83 memory_resource* set_default_resource(memory_resource* __r) noexcept; 84 85 // TODO 8.9 Pool resource classes 86 87 class memory_resource 88 { 89 static constexpr size_t _S_max_align = alignof(max_align_t); 90 91 public: 92 memory_resource() = default; 93 memory_resource(const memory_resource&) = default; 94 virtual ~memory_resource() = default; 95 96 memory_resource& operator=(const memory_resource&) = default; 97 98 _GLIBCXX_NODISCARD void* 99 allocate(size_t __bytes, size_t __alignment = _S_max_align) 100 { return do_allocate(__bytes, __alignment); } 101 102 void 103 deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align) 104 { return do_deallocate(__p, __bytes, __alignment); } 105 106 bool 107 is_equal(const memory_resource& __other) const noexcept 108 { return do_is_equal(__other); } 109 110 protected: 111 virtual void* 112 do_allocate(size_t __bytes, size_t __alignment) = 0; 113 114 virtual void 115 do_deallocate(void* __p, size_t __bytes, size_t __alignment) = 0; 116 117 virtual bool 118 do_is_equal(const memory_resource& __other) const noexcept = 0; 119 }; 120 121 inline bool 122 operator==(const memory_resource& __a, const memory_resource& __b) noexcept 123 { return &__a == &__b || __a.is_equal(__b); } 124 125 inline bool 126 operator!=(const memory_resource& __a, const memory_resource& __b) noexcept 127 { return !(__a == __b); } 128 129 130 template<typename _Tp> 131 class polymorphic_allocator 132 { 133 public: 134 using value_type = _Tp; 135 136 polymorphic_allocator() noexcept 137 : _M_resource(get_default_resource()) 138 { } 139 140 polymorphic_allocator(memory_resource* __r) 141 : _M_resource(__r) 142 { _GLIBCXX_DEBUG_ASSERT(__r); } 143 144 polymorphic_allocator(const polymorphic_allocator& __other) = default; 145 146 template <typename _Up> 147 polymorphic_allocator(const polymorphic_allocator<_Up>& 148 __other) noexcept 149 : _M_resource(__other.resource()) 150 { } 151 152 polymorphic_allocator& 153 operator=(const polymorphic_allocator& __rhs) = default; 154 155 _GLIBCXX_NODISCARD _Tp* allocate(size_t __n) 156 { return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp), 157 alignof(_Tp))); } 158 159 void 160 deallocate(_Tp* __p, size_t __n) 161 { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); } 162 163 template <typename _Tp1, typename... _Args> //used here 164 void 165 construct(_Tp1* __p, _Args&&... __args) 166 { 167 std::__uses_allocator_construct(this->resource(), __p, 168 std::forward<_Args>(__args)...); 169 } 170 171 // Specializations for pair using piecewise construction 172 template <typename _Tp1, typename _Tp2, 173 typename... _Args1, typename... _Args2> 174 void 175 construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t, 176 tuple<_Args1...> __x, tuple<_Args2...> __y) 177 { 178 memory_resource* const __resource = this->resource(); 179 auto __x_use_tag = 180 std::__use_alloc<_Tp1, memory_resource*, _Args1...>(__resource); 181 auto __y_use_tag = 182 std::__use_alloc<_Tp2, memory_resource*, _Args2...>(__resource); 183 184 ::new(__p) std::pair<_Tp1, _Tp2>(piecewise_construct, 185 _M_construct_p(__x_use_tag, __x), 186 _M_construct_p(__y_use_tag, __y)); 187 } 188 189 template <typename _Tp1, typename _Tp2> 190 void 191 construct(pair<_Tp1,_Tp2>* __p) 192 { this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); } 193 194 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp> 195 void 196 construct(pair<_Tp1,_Tp2>* __p, _Up&& __x, _Vp&& __y) 197 { 198 this->construct(__p, piecewise_construct, 199 forward_as_tuple(std::forward<_Up>(__x)), 200 forward_as_tuple(std::forward<_Vp>(__y))); 201 } 202 203 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp> 204 void 205 construct(pair<_Tp1,_Tp2>* __p, const std::pair<_Up, _Vp>& __pr) 206 { 207 this->construct(__p, piecewise_construct, 208 forward_as_tuple(__pr.first), 209 forward_as_tuple(__pr.second)); 210 } 211 212 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp> 213 void 214 construct(pair<_Tp1,_Tp2>* __p, pair<_Up, _Vp>&& __pr) 215 { 216 this->construct(__p, piecewise_construct, 217 forward_as_tuple(std::forward<_Up>(__pr.first)), 218 forward_as_tuple(std::forward<_Vp>(__pr.second))); 219 } 220 221 template <typename _Up> 222 void 223 destroy(_Up* __p) 224 { __p->~_Up(); } 225 226 // Return a default-constructed allocator (no allocator propagation) 227 polymorphic_allocator 228 select_on_container_copy_construction() const 229 { return polymorphic_allocator(); } 230 231 memory_resource* resource() const { return _M_resource; } 232 233 private: 234 using __uses_alloc1_ = __uses_alloc1<memory_resource*>; 235 using __uses_alloc2_ = __uses_alloc2<memory_resource*>; 236 237 template<typename _Tuple> 238 _Tuple&& 239 _M_construct_p(__uses_alloc0, _Tuple& __t) 240 { return std::move(__t); } 241 242 template<typename... _Args> 243 decltype(auto) 244 _M_construct_p(__uses_alloc1_ __ua, tuple<_Args...>& __t) 245 { return tuple_cat(make_tuple(allocator_arg, *(__ua._M_a)), 246 std::move(__t)); } 247 248 template<typename... _Args> 249 decltype(auto) 250 _M_construct_p(__uses_alloc2_ __ua, tuple<_Args...>& __t) 251 { return tuple_cat(std::move(__t), make_tuple(*(__ua._M_a))); } 252 253 memory_resource* _M_resource; 254 }; 255 256 template <class _Tp1, class _Tp2> 257 bool 258 operator==(const polymorphic_allocator<_Tp1>& __a, 259 const polymorphic_allocator<_Tp2>& __b) noexcept 260 { return *__a.resource() == *__b.resource(); } 261 262 template <class _Tp1, class _Tp2> 263 bool 264 operator!=(const polymorphic_allocator<_Tp1>& __a, 265 const polymorphic_allocator<_Tp2>& __b) noexcept 266 { return !(__a == __b); } 267 268 269 /// @cond undocumented 270 class __resource_adaptor_common 271 { 272 template<typename, typename> friend class __resource_adaptor_imp; 273 274 struct _AlignMgr 275 { 276 _AlignMgr(size_t __nbytes, size_t __align) 277 : _M_nbytes(__nbytes), _M_align(__align) 278 { } 279 280 // Total size that needs to be allocated. 281 size_t 282 _M_alloc_size() const { return _M_buf_size() + _M_token_size(); } 283 284 void* 285 _M_adjust(void* __ptr) const 286 { 287 const auto __orig_ptr = static_cast<char*>(__ptr); 288 size_t __space = _M_buf_size(); 289 // Align the pointer within the buffer: 290 std::align(_M_align, _M_nbytes, __ptr, __space); 291 const auto __aligned_ptr = static_cast<char*>(__ptr); 292 const auto __token_size = _M_token_size(); 293 // Store token immediately after the aligned block: 294 char* const __end = __aligned_ptr + _M_nbytes; 295 if (__token_size == 1) 296 _S_write<unsigned char>(__end, __aligned_ptr - __orig_ptr); 297 else if (__token_size == sizeof(short)) 298 _S_write<unsigned short>(__end, __aligned_ptr - __orig_ptr); 299 else if (__token_size == sizeof(int) && sizeof(int) < sizeof(char*)) 300 _S_write<unsigned int>(__end, __aligned_ptr - __orig_ptr); 301 else // (__token_size == sizeof(char*)) 302 // Just store the original pointer: 303 _S_write<char*>(__end, __orig_ptr); 304 return __aligned_ptr; 305 } 306 307 char* 308 _M_unadjust(char* __ptr) const 309 { 310 const char* const __end = __ptr + _M_nbytes; 311 char* __orig_ptr; 312 const auto __token_size = _M_token_size(); 313 // Read the token and restore the original pointer: 314 if (__token_size == 1) 315 __orig_ptr = __ptr - _S_read<unsigned char>(__end); 316 else if (__token_size == sizeof(short)) 317 __orig_ptr = __ptr - _S_read<unsigned short>(__end); 318 else if (__token_size == sizeof(int) 319 && sizeof(int) < sizeof(char*)) 320 __orig_ptr = __ptr - _S_read<unsigned int>(__end); 321 else // (__token_size == sizeof(char*)) 322 __orig_ptr = _S_read<char*>(__end); 323 // The adjustment is always less than the requested alignment, 324 // so if that isn't true now then either the wrong size was passed 325 // to deallocate or the token was overwritten by a buffer overflow: 326 __glibcxx_assert(static_cast<size_t>(__ptr - __orig_ptr) < _M_align); 327 return __orig_ptr; 328 } 329 330 private: 331 size_t _M_nbytes; 332 size_t _M_align; 333 334 // Number of bytes needed to fit block of given size and alignment. 335 size_t 336 _M_buf_size() const { return _M_nbytes + _M_align - 1; } 337 338 // Number of additional bytes needed to write the token. 339 int 340 _M_token_size() const 341 { 342 if (_M_align <= (1ul << __CHAR_BIT__)) 343 return 1; 344 if (_M_align <= (1ul << (sizeof(short) * __CHAR_BIT__))) 345 return sizeof(short); 346 if (_M_align <= (1ull << (sizeof(int) * __CHAR_BIT__))) 347 return sizeof(int); 348 return sizeof(char*); 349 } 350 351 template<typename _Tp> 352 static void 353 _S_write(void* __to, _Tp __val) 354 { __builtin_memcpy(__to, &__val, sizeof(_Tp)); } 355 356 template<typename _Tp> 357 static _Tp 358 _S_read(const void* __from) 359 { 360 _Tp __val; 361 __builtin_memcpy(&__val, __from, sizeof(_Tp)); 362 return __val; 363 } 364 }; 365 }; 366 /// @endcond 367 368 // 8.7.1 __resource_adaptor_imp 369 template<typename _Alloc, typename _Resource> 370 class __resource_adaptor_imp 371 : public _Resource, private __resource_adaptor_common 372 { 373 using memory_resource = _Resource; 374 375 static_assert(is_same<char, 376 typename allocator_traits<_Alloc>::value_type>::value, 377 "Allocator's value_type is char"); 378 static_assert(is_same<char*, 379 typename allocator_traits<_Alloc>::pointer>::value, 380 "Allocator's pointer type is value_type*"); 381 static_assert(is_same<const char*, 382 typename allocator_traits<_Alloc>::const_pointer>::value, 383 "Allocator's const_pointer type is value_type const*"); 384 static_assert(is_same<void*, 385 typename allocator_traits<_Alloc>::void_pointer>::value, 386 "Allocator's void_pointer type is void*"); 387 static_assert(is_same<const void*, 388 typename allocator_traits<_Alloc>::const_void_pointer>::value, 389 "Allocator's const_void_pointer type is void const*"); 390 391 public: 392 using allocator_type = _Alloc; 393 394 __resource_adaptor_imp() = default; 395 __resource_adaptor_imp(const __resource_adaptor_imp&) = default; 396 __resource_adaptor_imp(__resource_adaptor_imp&&) = default; 397 398 explicit __resource_adaptor_imp(const _Alloc& __a2) 399 : _M_alloc(__a2) 400 { } 401 402 explicit __resource_adaptor_imp(_Alloc&& __a2) 403 : _M_alloc(std::move(__a2)) 404 { } 405 406 __resource_adaptor_imp& 407 operator=(const __resource_adaptor_imp&) = default; 408 409 allocator_type get_allocator() const noexcept { return _M_alloc; } 410 411 protected: 412 virtual void* 413 do_allocate(size_t __bytes, size_t __alignment) override 414 { 415 // Cannot use max_align_t on 32-bit Solaris x86, see PR libstdc++/77691 416#if ! ((defined __sun__ || defined __VXWORKS__) && defined __i386__) 417 if (__alignment == alignof(max_align_t)) 418 return _M_allocate<alignof(max_align_t)>(__bytes); 419#endif 420 switch (__alignment) 421 { 422 case 1: 423 return _M_alloc.allocate(__bytes); 424 case 2: 425 return _M_allocate<2>(__bytes); 426 case 4: 427 return _M_allocate<4>(__bytes); 428 case 8: 429 return _M_allocate<8>(__bytes); 430 } 431 const _AlignMgr __mgr(__bytes, __alignment); 432 // Assume _M_alloc returns 1-byte aligned memory, so allocate enough 433 // space to fit a block of the right size and alignment, plus some 434 // extra bytes to store a token for retrieving the original pointer. 435 return __mgr._M_adjust(_M_alloc.allocate(__mgr._M_alloc_size())); 436 } 437 438 virtual void 439 do_deallocate(void* __ptr, size_t __bytes, size_t __alignment) noexcept 440 override 441 { 442#if ! ((defined __sun__ || defined __VXWORKS__) && defined __i386__) 443 if (__alignment == alignof(max_align_t)) 444 return (void) _M_deallocate<alignof(max_align_t)>(__ptr, __bytes); 445#endif 446 switch (__alignment) 447 { 448 case 1: 449 return (void) _M_alloc.deallocate((char*)__ptr, __bytes); 450 case 2: 451 return (void) _M_deallocate<2>(__ptr, __bytes); 452 case 4: 453 return (void) _M_deallocate<4>(__ptr, __bytes); 454 case 8: 455 return (void) _M_deallocate<8>(__ptr, __bytes); 456 } 457 const _AlignMgr __mgr(__bytes, __alignment); 458 // Use the stored token to retrieve the original pointer. 459 _M_alloc.deallocate(__mgr._M_unadjust((char*)__ptr), 460 __mgr._M_alloc_size()); 461 } 462 463 virtual bool 464 do_is_equal(const memory_resource& __other) const noexcept override 465 { 466 if (auto __p = dynamic_cast<const __resource_adaptor_imp*>(&__other)) 467 return _M_alloc == __p->_M_alloc; 468 return false; 469 } 470 471 private: 472 template<size_t _Num> 473 struct _Aligned_type { alignas(_Num) char __c[_Num]; }; 474 475 // Rebind the allocator to the specified type and use it to allocate. 476 template<size_t _Num, typename _Tp = _Aligned_type<_Num>> 477 void* 478 _M_allocate(size_t __bytes) 479 { 480 typename allocator_traits<_Alloc>::template 481 rebind_alloc<_Tp> __a2(_M_alloc); 482 const size_t __n = (__bytes + _Num - 1) / _Num; 483 return __a2.allocate(__n); 484 } 485 486 // Rebind the allocator to the specified type and use it to deallocate. 487 template<size_t _Num, typename _Tp = _Aligned_type<_Num>> 488 void 489 _M_deallocate(void* __ptr, size_t __bytes) noexcept 490 { 491 typename allocator_traits<_Alloc>::template 492 rebind_alloc<_Tp> __a2(_M_alloc); 493 const size_t __n = (__bytes + _Num - 1) / _Num; 494 __a2.deallocate((_Tp*)__ptr, __n); 495 } 496 497 _Alloc _M_alloc{}; 498 }; 499 500 // Global memory resources 501 502 inline memory_resource* 503 new_delete_resource() noexcept 504 { 505 using type = resource_adaptor<__gnu_cxx::new_allocator<char>>; 506 alignas(type) static unsigned char __buf[sizeof(type)]; 507 static type* __r = new(__buf) type; 508 return __r; 509 } 510 511 inline memory_resource* 512 null_memory_resource() noexcept 513 { 514 class type final : public memory_resource 515 { 516 void* 517 do_allocate(size_t, size_t) override 518 { std::__throw_bad_alloc(); } 519 520 void 521 do_deallocate(void*, size_t, size_t) noexcept override 522 { } 523 524 bool 525 do_is_equal(const memory_resource& __other) const noexcept override 526 { return this == &__other; } 527 }; 528 529 alignas(type) static unsigned char __buf[sizeof(type)]; 530 static type* __r = new(__buf) type; 531 return __r; 532 } 533 534 // The default memory resource 535 536 /// @cond undocumented 537 inline std::atomic<memory_resource*>& 538 __get_default_resource() 539 { 540 using type = atomic<memory_resource*>; 541 alignas(type) static unsigned char __buf[sizeof(type)]; 542 static type* __r = new(__buf) type(new_delete_resource()); 543 return *__r; 544 } 545 /// @endcond 546 547 /// Get the current default resource. 548 inline memory_resource* 549 get_default_resource() noexcept 550 { return __get_default_resource().load(); } 551 552 /// Change the default resource and return the previous one. 553 inline memory_resource* 554 set_default_resource(memory_resource* __r) noexcept 555 { 556 if (__r == nullptr) 557 __r = new_delete_resource(); 558 return __get_default_resource().exchange(__r); 559 } 560 561} // namespace pmr 562} // namespace fundamentals_v2 563} // namespace experimental 564 565_GLIBCXX_END_NAMESPACE_VERSION 566} // namespace std 567#endif // C++14 568#endif // _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE 569