xref: /netbsd-src/external/gpl3/gcc.old/dist/libstdc++-v3/include/std/memory_resource (revision 82d56013d7b633d116a93943de88e08335357a7c)
1// <memory_resource> -*- C++ -*-
2
3// Copyright (C) 2018-2019 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 include/memory_resource
26 *  This is a Standard C++ Library header.
27 */
28
29#ifndef _GLIBCXX_MEMORY_RESOURCE
30#define _GLIBCXX_MEMORY_RESOURCE 1
31
32#pragma GCC system_header
33
34#if __cplusplus >= 201703L
35
36#include <limits>			// numeric_limits
37#include <memory>			// align, allocator_arg_t, __uses_alloc
38#include <utility>			// pair, index_sequence
39#include <vector>			// vector
40#include <cstddef>			// size_t, max_align_t, byte
41#include <shared_mutex>			// shared_mutex
42#include <bits/functexcept.h>
43#include <debug/assertions.h>
44
45namespace std _GLIBCXX_VISIBILITY(default)
46{
47_GLIBCXX_BEGIN_NAMESPACE_VERSION
48namespace pmr
49{
50#ifdef _GLIBCXX_HAS_GTHREADS
51  // Header and all contents are present.
52# define __cpp_lib_memory_resource 201603
53#else
54  // The pmr::synchronized_pool_resource type is missing.
55# define __cpp_lib_memory_resource 1
56#endif
57
58  class memory_resource;
59
60#if __cplusplus == 201703L
61  template<typename _Tp>
62    class polymorphic_allocator;
63#else // C++20
64  template<typename _Tp = std::byte>
65    class polymorphic_allocator;
66#endif
67
68  // Global memory resources
69  memory_resource* new_delete_resource() noexcept;
70  memory_resource* null_memory_resource() noexcept;
71  memory_resource* set_default_resource(memory_resource* __r) noexcept;
72  memory_resource* get_default_resource() noexcept
73    __attribute__((__returns_nonnull__));
74
75  // Pool resource classes
76  struct pool_options;
77#ifdef _GLIBCXX_HAS_GTHREADS
78  class synchronized_pool_resource;
79#endif
80  class unsynchronized_pool_resource;
81  class monotonic_buffer_resource;
82
83  /// Class memory_resource
84  class memory_resource
85  {
86    static constexpr size_t _S_max_align = alignof(max_align_t);
87
88  public:
89    memory_resource() = default;
90    memory_resource(const memory_resource&) = default;
91    virtual ~memory_resource(); // key function
92
93    memory_resource& operator=(const memory_resource&) = default;
94
95    [[nodiscard]]
96    void*
97    allocate(size_t __bytes, size_t __alignment = _S_max_align)
98    __attribute__((__returns_nonnull__,__alloc_size__(2),__alloc_align__(3)))
99    { return do_allocate(__bytes, __alignment); }
100
101    void
102    deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align)
103    __attribute__((__nonnull__))
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  private:
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  // C++17 23.12.3 Class template polymorphic_allocator
131  template<typename _Tp>
132    class polymorphic_allocator
133    {
134      // _GLIBCXX_RESOLVE_LIB_DEFECTS
135      // 2975. Missing case for pair construction in polymorphic allocators
136      template<typename _Up>
137	struct __not_pair { using type = void; };
138
139      template<typename _Up1, typename _Up2>
140	struct __not_pair<pair<_Up1, _Up2>> { };
141
142    public:
143      using value_type = _Tp;
144
145      polymorphic_allocator() noexcept
146      : _M_resource(get_default_resource())
147      { }
148
149      polymorphic_allocator(memory_resource* __r) noexcept
150      __attribute__((__nonnull__))
151      : _M_resource(__r)
152      { _GLIBCXX_DEBUG_ASSERT(__r); }
153
154      polymorphic_allocator(const polymorphic_allocator& __other) = default;
155
156      template<typename _Up>
157	polymorphic_allocator(const polymorphic_allocator<_Up>& __x) noexcept
158	: _M_resource(__x.resource())
159	{ }
160
161      polymorphic_allocator&
162      operator=(const polymorphic_allocator&) = delete;
163
164      [[nodiscard]]
165      _Tp*
166      allocate(size_t __n)
167      __attribute__((__returns_nonnull__))
168      {
169	if (__n > (numeric_limits<size_t>::max() / sizeof(_Tp)))
170	  std::__throw_bad_alloc();
171	return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp),
172						       alignof(_Tp)));
173      }
174
175      void
176      deallocate(_Tp* __p, size_t __n) noexcept
177      __attribute__((__nonnull__))
178      { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); }
179
180#if __cplusplus > 201703L
181      void*
182      allocate_bytes(size_t __nbytes,
183		     size_t __alignment = alignof(max_align_t))
184      { return _M_resource->allocate(__nbytes, __alignment); }
185
186      void
187      deallocate_bytes(void* __p, size_t __nbytes,
188		       size_t __alignment = alignof(max_align_t))
189      { _M_resource->deallocate(__p, __nbytes, __alignment); }
190
191      template<typename _Up>
192	_Up*
193	allocate_object(size_t __n = 1)
194	{
195	  if ((std::numeric_limits<size_t>::max() / sizeof(_Up)) < __n)
196	    __throw_length_error("polymorphic_allocator::allocate_object");
197	  return static_cast<_Up*>(allocate_bytes(__n * sizeof(_Up),
198						  alignof(_Up)));
199	}
200
201      template<typename _Up>
202	void
203	deallocate_object(_Up* __p, size_t __n = 1)
204	{ deallocate_bytes(__p, __n * sizeof(_Up), alignof(_Up)); }
205
206      template<typename _Up, typename... _CtorArgs>
207	_Up*
208	new_object(_CtorArgs&&... __ctor_args)
209	{
210	  _Up* __p = allocate_object<_Up>();
211	  __try
212	    {
213	      construct(__p, std::forward<_CtorArgs>(__ctor_args)...);
214	    }
215	  __catch (...)
216	    {
217	      deallocate_object(__p);
218	      __throw_exception_again;
219	    }
220	  return __p;
221	}
222
223      template<typename _Up>
224	void
225	delete_object(_Up* __p)
226	{
227	  destroy(__p);
228	  deallocate_object(__p);
229	}
230#endif // C++2a
231
232#if __cplusplus == 201703L
233      template<typename _Tp1, typename... _Args>
234	__attribute__((__nonnull__))
235	typename __not_pair<_Tp1>::type
236	construct(_Tp1* __p, _Args&&... __args)
237	{
238	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
239	  // 2969. polymorphic_allocator::construct() shouldn't pass resource()
240	  using __use_tag
241	    = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>;
242	  if constexpr (is_base_of_v<__uses_alloc0, __use_tag>)
243	    ::new(__p) _Tp1(std::forward<_Args>(__args)...);
244	  else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>)
245	    ::new(__p) _Tp1(allocator_arg, *this,
246			    std::forward<_Args>(__args)...);
247	  else
248	    ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this);
249	}
250
251      template<typename _Tp1, typename _Tp2,
252	       typename... _Args1, typename... _Args2>
253	__attribute__((__nonnull__))
254	void
255	construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t,
256		  tuple<_Args1...> __x, tuple<_Args2...> __y)
257	{
258	  auto __x_tag =
259	    __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this);
260	  auto __y_tag =
261	    __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this);
262	  index_sequence_for<_Args1...> __x_i;
263	  index_sequence_for<_Args2...> __y_i;
264
265	  ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct,
266				      _S_construct_p(__x_tag, __x_i, __x),
267				      _S_construct_p(__y_tag, __y_i, __y));
268	}
269
270      template<typename _Tp1, typename _Tp2>
271	__attribute__((__nonnull__))
272	void
273	construct(pair<_Tp1, _Tp2>* __p)
274	{ this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
275
276      template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
277	__attribute__((__nonnull__))
278	void
279	construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y)
280	{
281	  this->construct(__p, piecewise_construct,
282			  forward_as_tuple(std::forward<_Up>(__x)),
283			  forward_as_tuple(std::forward<_Vp>(__y)));
284	}
285
286      template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
287	__attribute__((__nonnull__))
288	void
289	construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr)
290	{
291	  this->construct(__p, piecewise_construct,
292			  forward_as_tuple(__pr.first),
293			  forward_as_tuple(__pr.second));
294	}
295
296      template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
297	__attribute__((__nonnull__))
298	void
299	construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr)
300	{
301	  this->construct(__p, piecewise_construct,
302			  forward_as_tuple(std::forward<_Up>(__pr.first)),
303			  forward_as_tuple(std::forward<_Vp>(__pr.second)));
304	}
305#else
306      template<typename _Tp1, typename... _Args>
307	__attribute__((__nonnull__))
308	void
309	construct(_Tp1* __p, _Args&&... __args)
310	{
311	  std::uninitialized_construct_using_allocator(__p, *this,
312	      std::forward<_Args>(__args)...);
313	}
314#endif
315
316      template<typename _Up>
317	__attribute__((__nonnull__))
318	void
319	destroy(_Up* __p)
320	{ __p->~_Up(); }
321
322      polymorphic_allocator
323      select_on_container_copy_construction() const noexcept
324      { return polymorphic_allocator(); }
325
326      memory_resource*
327      resource() const noexcept
328      __attribute__((__returns_nonnull__))
329      { return _M_resource; }
330
331    private:
332      using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
333      using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
334
335      template<typename _Ind, typename... _Args>
336	static tuple<_Args&&...>
337	_S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
338	{ return std::move(__t); }
339
340      template<size_t... _Ind, typename... _Args>
341	static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
342	_S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
343		       tuple<_Args...>& __t)
344	{
345	  return {
346	      allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
347	  };
348	}
349
350      template<size_t... _Ind, typename... _Args>
351	static tuple<_Args&&..., polymorphic_allocator>
352	_S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
353		       tuple<_Args...>& __t)
354	{ return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
355
356      memory_resource* _M_resource;
357    };
358
359  template<typename _Tp1, typename _Tp2>
360    inline bool
361    operator==(const polymorphic_allocator<_Tp1>& __a,
362	       const polymorphic_allocator<_Tp2>& __b) noexcept
363    { return *__a.resource() == *__b.resource(); }
364
365  template<typename _Tp1, typename _Tp2>
366    inline bool
367    operator!=(const polymorphic_allocator<_Tp1>& __a,
368	       const polymorphic_allocator<_Tp2>& __b) noexcept
369    { return !(__a == __b); }
370
371
372  /// Parameters for tuning a pool resource's behaviour.
373  struct pool_options
374  {
375    /** @brief Upper limit on number of blocks in a chunk.
376     *
377     * A lower value prevents allocating huge chunks that could remain mostly
378     * unused, but means pools will need to replenished more frequently.
379     */
380    size_t max_blocks_per_chunk = 0;
381
382    /* @brief Largest block size (in bytes) that should be served from pools.
383     *
384     * Larger allocations will be served directly by the upstream resource,
385     * not from one of the pools managed by the pool resource.
386     */
387    size_t largest_required_pool_block = 0;
388  };
389
390  // Common implementation details for un-/synchronized pool resources.
391  class __pool_resource
392  {
393    friend class synchronized_pool_resource;
394    friend class unsynchronized_pool_resource;
395
396    __pool_resource(const pool_options& __opts, memory_resource* __upstream);
397
398    ~__pool_resource();
399
400    __pool_resource(const __pool_resource&) = delete;
401    __pool_resource& operator=(const __pool_resource&) = delete;
402
403    // Allocate a large unpooled block.
404    void*
405    allocate(size_t __bytes, size_t __alignment);
406
407    // Deallocate a large unpooled block.
408    void
409    deallocate(void* __p, size_t __bytes, size_t __alignment);
410
411
412    // Deallocate unpooled memory.
413    void release() noexcept;
414
415    memory_resource* resource() const noexcept
416    { return _M_unpooled.get_allocator().resource(); }
417
418    struct _Pool;
419
420    _Pool* _M_alloc_pools();
421
422    const pool_options _M_opts;
423
424    struct _BigBlock;
425    // Collection of blocks too big for any pool, sorted by address.
426    // This also stores the only copy of the upstream memory resource pointer.
427    _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled;
428
429    const int _M_npools;
430  };
431
432#ifdef _GLIBCXX_HAS_GTHREADS
433  /// A thread-safe memory resource that manages pools of fixed-size blocks.
434  class synchronized_pool_resource : public memory_resource
435  {
436  public:
437    synchronized_pool_resource(const pool_options& __opts,
438				 memory_resource* __upstream)
439    __attribute__((__nonnull__));
440
441    synchronized_pool_resource()
442    : synchronized_pool_resource(pool_options(), get_default_resource())
443    { }
444
445    explicit
446    synchronized_pool_resource(memory_resource* __upstream)
447    __attribute__((__nonnull__))
448    : synchronized_pool_resource(pool_options(), __upstream)
449    { }
450
451    explicit
452    synchronized_pool_resource(const pool_options& __opts)
453    : synchronized_pool_resource(__opts, get_default_resource()) { }
454
455    synchronized_pool_resource(const synchronized_pool_resource&) = delete;
456
457    virtual ~synchronized_pool_resource();
458
459    synchronized_pool_resource&
460    operator=(const synchronized_pool_resource&) = delete;
461
462    void release();
463
464    memory_resource*
465    upstream_resource() const noexcept
466    __attribute__((__returns_nonnull__))
467    { return _M_impl.resource(); }
468
469    pool_options options() const noexcept { return _M_impl._M_opts; }
470
471  protected:
472    void*
473    do_allocate(size_t __bytes, size_t __alignment) override;
474
475    void
476    do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
477
478    bool
479    do_is_equal(const memory_resource& __other) const noexcept override
480    { return this == &__other; }
481
482  public:
483    // Thread-specific pools (only public for access by implementation details)
484    struct _TPools;
485
486  private:
487    _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&);
488    _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&);
489    auto _M_thread_specific_pools() noexcept;
490
491    __pool_resource _M_impl;
492    __gthread_key_t _M_key;
493    // Linked list of thread-specific pools. All threads share _M_tpools[0].
494    _TPools* _M_tpools = nullptr;
495    mutable shared_mutex _M_mx;
496  };
497#endif
498
499  /// A non-thread-safe memory resource that manages pools of fixed-size blocks.
500  class unsynchronized_pool_resource : public memory_resource
501  {
502  public:
503    [[__gnu__::__nonnull__]]
504    unsynchronized_pool_resource(const pool_options& __opts,
505				 memory_resource* __upstream);
506
507    unsynchronized_pool_resource()
508    : unsynchronized_pool_resource(pool_options(), get_default_resource())
509    { }
510
511    [[__gnu__::__nonnull__]]
512    explicit
513    unsynchronized_pool_resource(memory_resource* __upstream)
514    : unsynchronized_pool_resource(pool_options(), __upstream)
515    { }
516
517    explicit
518    unsynchronized_pool_resource(const pool_options& __opts)
519    : unsynchronized_pool_resource(__opts, get_default_resource()) { }
520
521    unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
522
523    virtual ~unsynchronized_pool_resource();
524
525    unsynchronized_pool_resource&
526    operator=(const unsynchronized_pool_resource&) = delete;
527
528    void release();
529
530    [[__gnu__::__returns_nonnull__]]
531    memory_resource*
532    upstream_resource() const noexcept
533    { return _M_impl.resource(); }
534
535    pool_options options() const noexcept { return _M_impl._M_opts; }
536
537  protected:
538    void*
539    do_allocate(size_t __bytes, size_t __alignment) override;
540
541    void
542    do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
543
544    bool
545    do_is_equal(const memory_resource& __other) const noexcept override
546    { return this == &__other; }
547
548  private:
549    using _Pool = __pool_resource::_Pool;
550
551    auto _M_find_pool(size_t) noexcept;
552
553    __pool_resource _M_impl;
554    _Pool* _M_pools = nullptr;
555  };
556
557  class monotonic_buffer_resource : public memory_resource
558  {
559  public:
560    explicit
561    monotonic_buffer_resource(memory_resource* __upstream) noexcept
562    __attribute__((__nonnull__))
563    : _M_upstream(__upstream)
564    { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }
565
566    monotonic_buffer_resource(size_t __initial_size,
567			      memory_resource* __upstream) noexcept
568    __attribute__((__nonnull__))
569    : _M_next_bufsiz(__initial_size),
570      _M_upstream(__upstream)
571    {
572      _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
573      _GLIBCXX_DEBUG_ASSERT(__initial_size > 0);
574    }
575
576    monotonic_buffer_resource(void* __buffer, size_t __buffer_size,
577			      memory_resource* __upstream) noexcept
578    __attribute__((__nonnull__(4)))
579    : _M_current_buf(__buffer), _M_avail(__buffer_size),
580      _M_next_bufsiz(_S_next_bufsize(__buffer_size)),
581      _M_upstream(__upstream),
582      _M_orig_buf(__buffer), _M_orig_size(__buffer_size)
583    {
584      _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
585      _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0);
586    }
587
588    monotonic_buffer_resource() noexcept
589    : monotonic_buffer_resource(get_default_resource())
590    { }
591
592    explicit
593    monotonic_buffer_resource(size_t __initial_size) noexcept
594    : monotonic_buffer_resource(__initial_size, get_default_resource())
595    { }
596
597    monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept
598    : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource())
599    { }
600
601    monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
602
603    virtual ~monotonic_buffer_resource(); // key function
604
605    monotonic_buffer_resource&
606    operator=(const monotonic_buffer_resource&) = delete;
607
608    void
609    release() noexcept
610    {
611      if (_M_head)
612	_M_release_buffers();
613
614      // reset to initial state at contruction:
615      if ((_M_current_buf = _M_orig_buf))
616	{
617	  _M_avail = _M_orig_size;
618	  _M_next_bufsiz = _S_next_bufsize(_M_orig_size);
619	}
620      else
621	{
622	  _M_avail = 0;
623	  _M_next_bufsiz = _M_orig_size;
624	}
625    }
626
627    memory_resource*
628    upstream_resource() const noexcept
629    __attribute__((__returns_nonnull__))
630    { return _M_upstream; }
631
632  protected:
633    void*
634    do_allocate(size_t __bytes, size_t __alignment) override
635    {
636      if (__bytes == 0)
637	__bytes = 1; // Ensures we don't return the same pointer twice.
638
639      void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail);
640      if (!__p)
641	{
642	  _M_new_buffer(__bytes, __alignment);
643	  __p = _M_current_buf;
644	}
645      _M_current_buf = (char*)_M_current_buf + __bytes;
646      _M_avail -= __bytes;
647      return __p;
648    }
649
650    void
651    do_deallocate(void*, size_t, size_t) override
652    { }
653
654    bool
655    do_is_equal(const memory_resource& __other) const noexcept override
656    { return this == &__other; }
657
658  private:
659    // Update _M_current_buf and _M_avail to refer to a new buffer with
660    // at least the specified size and alignment, allocated from upstream.
661    void
662    _M_new_buffer(size_t __bytes, size_t __alignment);
663
664    // Deallocate all buffers obtained from upstream.
665    void
666    _M_release_buffers() noexcept;
667
668    static size_t
669    _S_next_bufsize(size_t __buffer_size) noexcept
670    {
671      if (__buffer_size == 0)
672	__buffer_size = 1;
673      return __buffer_size * _S_growth_factor;
674    }
675
676    static constexpr size_t _S_init_bufsize = 128 * sizeof(void*);
677    static constexpr float _S_growth_factor = 1.5;
678
679    void*	_M_current_buf = nullptr;
680    size_t	_M_avail = 0;
681    size_t	_M_next_bufsiz = _S_init_bufsize;
682
683    // Initial values set at construction and reused by release():
684    memory_resource* const	_M_upstream;
685    void* const			_M_orig_buf = nullptr;
686    size_t const		_M_orig_size = _M_next_bufsiz;
687
688    class _Chunk;
689    _Chunk* _M_head = nullptr;
690  };
691
692} // namespace pmr
693_GLIBCXX_END_NAMESPACE_VERSION
694} // namespace std
695
696#endif // C++17
697#endif // _GLIBCXX_MEMORY_RESOURCE
698