xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/src/c++17/memory_resource.cc (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 // <memory_resource> implementation -*- C++ -*-
2 
3 // Copyright (C) 2018-2022 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 #include <memory_resource>
26 #include <algorithm>			// lower_bound, rotate
27 #include <atomic>
28 #include <bit>				// has_single_bit, bit_ceil, bit_width
29 #include <new>
30 #if ATOMIC_POINTER_LOCK_FREE != 2
31 # include <bits/std_mutex.h>	// std::mutex, std::lock_guard
32 # include <bits/move.h>		// std::__exchange
33 #endif
34 
35 #if __has_cpp_attribute(clang::require_constant_initialization)
36 #  define __constinit [[clang::require_constant_initialization]]
37 #endif
38 
39 namespace std _GLIBCXX_VISIBILITY(default)
40 {
41 _GLIBCXX_BEGIN_NAMESPACE_VERSION
42 namespace pmr
43 {
44   // This was defined inline in 9.1 and 9.2 so code compiled by those
45   // versions will not use this symbol.
46   memory_resource::~memory_resource() = default;
47 
48   namespace
49   {
50     class newdel_res_t final : public memory_resource
51     {
52       void*
53       do_allocate(size_t __bytes, size_t __alignment) override
54       { return ::operator new(__bytes, std::align_val_t(__alignment)); }
55 
56       void
57       do_deallocate(void* __p, size_t __bytes, size_t __alignment) noexcept
58       override
59       { ::operator delete(__p, __bytes, std::align_val_t(__alignment)); }
60 
61       bool
62       do_is_equal(const memory_resource& __other) const noexcept override
63       { return &__other == this; }
64     };
65 
66     class null_res_t final : public memory_resource
67     {
68       void*
69       do_allocate(size_t, size_t) override
70       { std::__throw_bad_alloc(); }
71 
72       void
73       do_deallocate(void*, size_t, size_t) noexcept override
74       { }
75 
76       bool
77       do_is_equal(const memory_resource& __other) const noexcept override
78       { return &__other == this; }
79     };
80 
81     template<typename T>
82       struct constant_init
83       {
84 	union {
85 	  unsigned char unused;
86 	  T obj;
87 	};
88 	constexpr constant_init() : obj() { }
89 
90 	template<typename U>
91 	  explicit constexpr constant_init(U arg) : obj(arg) { }
92 
93 	~constant_init() { /* do nothing, union member is not destroyed */ }
94       };
95 
96     __constinit constant_init<newdel_res_t> newdel_res{};
97     __constinit constant_init<null_res_t> null_res{};
98 #if ATOMIC_POINTER_LOCK_FREE == 2
99     using atomic_mem_res = atomic<memory_resource*>;
100 # define _GLIBCXX_ATOMIC_MEM_RES_CAN_BE_CONSTANT_INITIALIZED
101 #elif defined(_GLIBCXX_HAS_GTHREADS)
102     // Can't use pointer-width atomics, define a type using a mutex instead:
103     struct atomic_mem_res
104     {
105 # ifdef __GTHREAD_MUTEX_INIT
106 #  define _GLIBCXX_ATOMIC_MEM_RES_CAN_BE_CONSTANT_INITIALIZED
107       // std::mutex has constexpr constructor
108       constexpr
109 # endif
110       atomic_mem_res(memory_resource* r) : val(r) { }
111 
112       mutex mx;
113       memory_resource* val;
114 
115       memory_resource* load()
116       {
117 	lock_guard<mutex> lock(mx);
118 	return val;
119       }
120 
121       memory_resource* exchange(memory_resource* r)
122       {
123 	lock_guard<mutex> lock(mx);
124 	return std::__exchange(val, r);
125       }
126     };
127 #else
128 # define _GLIBCXX_ATOMIC_MEM_RES_CAN_BE_CONSTANT_INITIALIZED
129     // Single-threaded, no need for synchronization
130     struct atomic_mem_res
131     {
132       constexpr
133       atomic_mem_res(memory_resource* r) : val(r) { }
134 
135       memory_resource* val;
136 
137       memory_resource* load() const
138       {
139 	return val;
140       }
141 
142       memory_resource* exchange(memory_resource* r)
143       {
144 	return std::__exchange(val, r);
145       }
146     };
147 #endif // ATOMIC_POINTER_LOCK_FREE == 2
148 
149 #ifdef _GLIBCXX_ATOMIC_MEM_RES_CAN_BE_CONSTANT_INITIALIZED
150     __constinit constant_init<atomic_mem_res> default_res{&newdel_res.obj};
151 #else
152 # include "default_resource.h"
153 #endif
154   } // namespace
155 
156   memory_resource*
157   new_delete_resource() noexcept
158   { return &newdel_res.obj; }
159 
160   memory_resource*
161   null_memory_resource() noexcept
162   { return &null_res.obj; }
163 
164   memory_resource*
165   set_default_resource(memory_resource* r) noexcept
166   {
167     if (r == nullptr)
168       r = new_delete_resource();
169     return default_res.obj.exchange(r);
170   }
171 
172   memory_resource*
173   get_default_resource() noexcept
174   { return default_res.obj.load(); }
175 
176   // Member functions for std::pmr::monotonic_buffer_resource
177 
178   // This was defined inline in 9.1 and 9.2 so code compiled by those
179   // versions will not use this symbol.
180   monotonic_buffer_resource::~monotonic_buffer_resource() { release(); }
181 
182   namespace {
183 
184   // aligned_size<N> stores the size and alignment of a memory allocation.
185   // The size must be a multiple of N, leaving the low log2(N) bits free
186   // to store the base-2 logarithm of the alignment.
187   // For example, allocate(1024, 32) is stored as 1024 + log2(32) = 1029.
188   template<unsigned N>
189   struct aligned_size
190   {
191     // N must be a power of two
192     static_assert( std::__popcount(N) == 1 );
193 
194     static constexpr size_t _S_align_mask = N - 1;
195     static constexpr size_t _S_size_mask = ~_S_align_mask;
196 
197     constexpr
198     aligned_size(size_t sz, size_t align) noexcept
199     : value(sz | (std::__bit_width(align) - 1u))
200     {
201       __glibcxx_assert(size() == sz); // sz must be a multiple of N
202     }
203 
204     constexpr size_t
205     size() const noexcept
206     { return value & _S_size_mask; }
207 
208     constexpr size_t
209     alignment() const noexcept
210     { return size_t(1) << (value & _S_align_mask); }
211 
212     size_t value; // size | log2(alignment)
213   };
214 
215   // Round n up to a multiple of alignment, which must be a power of two.
216   constexpr size_t aligned_ceil(size_t n, size_t alignment)
217   {
218     return (n + alignment - 1) & ~(alignment - 1);
219   }
220 
221   } // namespace
222 
223   // Memory allocated by the upstream resource is managed in a linked list
224   // of _Chunk objects. A _Chunk object recording the size and alignment of
225   // the allocated block and a pointer to the previous chunk is placed
226   // at end of the block.
227   class monotonic_buffer_resource::_Chunk
228   {
229   public:
230     // Return the address and size of a block of memory allocated from __r,
231     // of at least __size bytes and aligned to __align.
232     // Add a new _Chunk to the front of the linked list at __head.
233     static pair<void*, size_t>
234     allocate(memory_resource* __r, size_t __size, size_t __align,
235 	     _Chunk*& __head)
236     {
237       const size_t __orig_size = __size;
238 
239       // Add space for the _Chunk object and round up to 64 bytes.
240       __size = aligned_ceil(__size + sizeof(_Chunk), 64);
241 
242       // Check for unsigned wraparound
243       if (__size < __orig_size) [[unlikely]]
244 	{
245 	  // monotonic_buffer_resource::do_allocate is not allowed to throw.
246 	  // If the required size is too large for size_t then ask the
247 	  // upstream resource for an impossibly large size and alignment.
248 	  __size = -1;
249 	  __align = ~(size_t(-1) >> 1);
250 	}
251 
252       void* __p = __r->allocate(__size, __align);
253 
254       // Add a chunk defined by (__p, __size, __align) to linked list __head.
255       // We know the end of the buffer is suitably-aligned for a _Chunk
256       // because the caller ensured __align is at least alignof(max_align_t).
257       void* const __back = (char*)__p + __size - sizeof(_Chunk);
258       __head = ::new(__back) _Chunk(__size, __align, __head);
259       return { __p, __size - sizeof(_Chunk) };
260     }
261 
262     // Return every chunk in linked list __head to resource __r.
263     static void
264     release(_Chunk*& __head, memory_resource* __r) noexcept
265     {
266       _Chunk* __next = __head;
267       __head = nullptr;
268       while (__next)
269 	{
270 	  _Chunk* __ch = __next;
271 	  __next = __ch->_M_next;
272 	  size_t __size = __ch->_M_size.size();
273 	  size_t __align = __ch->_M_size.alignment();
274 	  void* __start = (char*)(__ch + 1) - __size;
275 	  __r->deallocate(__start, __size, __align);
276 	}
277     }
278 
279   private:
280     _Chunk(size_t __size, size_t __align, _Chunk* __next) noexcept
281     : _M_size(__size, __align), _M_next(__next)
282     { }
283 
284     aligned_size<64> _M_size;
285     _Chunk* _M_next;
286   };
287 
288   void
289   monotonic_buffer_resource::_M_new_buffer(size_t bytes, size_t alignment)
290   {
291     const size_t n = std::max(bytes, _M_next_bufsiz);
292     const size_t m = aligned_ceil(alignment, alignof(std::max_align_t));
293     auto [p, size] = _Chunk::allocate(_M_upstream, n, m, _M_head);
294     _M_current_buf = p;
295     _M_avail = size;
296     _M_next_bufsiz *= _S_growth_factor;
297   }
298 
299   void
300   monotonic_buffer_resource::_M_release_buffers() noexcept
301   {
302     _Chunk::release(_M_head, _M_upstream);
303   }
304 
305   // Helper types for synchronized_pool_resource & unsynchronized_pool_resource
306 
307   namespace {
308 
309   // Simple bitset with runtime size.
310   // Tracks which blocks in a pool chunk are used/unused.
311   struct bitset
312   {
313     using word = uint64_t;
314     using size_type // unsigned integer type with no more than 32 bits
315       = conditional_t<numeric_limits<size_t>::digits <= 32, size_t, uint32_t>;
316 
317     static constexpr unsigned bits_per_word = numeric_limits<word>::digits;
318 
319     // The bitset does not own p
320     bitset(void* p, size_type num_blocks)
321     : _M_words(static_cast<word*>(p)), _M_size(num_blocks),
322       _M_next_word(0)
323     {
324       const size_type last_word = num_blocks / bits_per_word;
325       __builtin_memset(_M_words, 0, last_word * sizeof(*_M_words));
326       // Set bits beyond _M_size, so they are not treated as free blocks:
327       if (const size_type extra_bits = num_blocks % bits_per_word)
328 	_M_words[last_word] = word(-1) << extra_bits;
329       __glibcxx_assert( empty() );
330       __glibcxx_assert( free() == num_blocks );
331     }
332 
333     bitset() = default;
334     ~bitset() = default;
335 
336     // Number of blocks
337     size_type size() const noexcept { return _M_size; }
338 
339     // Number of free blocks (unset bits)
340     size_type free() const noexcept
341     {
342       size_type n = 0;
343       for (size_type i = _M_next_word; i < nwords(); ++i)
344 	n += (bits_per_word - std::__popcount(_M_words[i]));
345       return n;
346     }
347 
348     // True if there are no free blocks (all bits are set)
349     bool full() const noexcept
350     {
351       if (_M_next_word >= nwords())
352 	return true;
353       // For a bitset with size() > (max_blocks_per_chunk() - 64) we will
354       // have nwords() == (max_word_index() + 1) and so _M_next_word will
355       // never be equal to nwords().
356       // In that case, check if the last word is full:
357       if (_M_next_word == max_word_index())
358 	return _M_words[_M_next_word] == word(-1);
359       return false;
360     }
361 
362     // True if size() != 0 and all blocks are free (no bits are set).
363     bool empty() const noexcept
364     {
365       if (nwords() == 0)
366 	return false;
367       if (_M_next_word != 0)
368 	return false;
369       for (size_type i = 0; i < nwords() - 1; ++i)
370 	if (_M_words[i] != 0)
371 	  return false;
372       word last = _M_words[nwords() - 1];
373       if (const size_type extra_bits = size() % bits_per_word)
374 	last <<= (bits_per_word - extra_bits);
375       return last == 0;
376     }
377 
378     void reset() noexcept
379     {
380       _M_words = nullptr;
381       _M_size = _M_next_word = 0;
382     }
383 
384     bool operator[](size_type n) const noexcept
385     {
386       __glibcxx_assert( n < _M_size );
387       const size_type wd = n / bits_per_word;
388       const word bit = word(1) << (n % bits_per_word);
389       return _M_words[wd] & bit;
390     }
391 
392     size_type get_first_unset() noexcept
393     {
394       const size_type wd = _M_next_word;
395       if (wd < nwords())
396 	{
397 	  const size_type n = std::__countr_one(_M_words[wd]);
398 	  if (n < bits_per_word)
399 	    {
400 	      const word bit = word(1) << n;
401 	      _M_words[wd] |= bit;
402 	      update_next_word();
403 	      return (wd * bits_per_word) + n;
404 	    }
405 	}
406       return size_type(-1);
407     }
408 
409     void set(size_type n) noexcept
410     {
411       __glibcxx_assert( n < _M_size );
412       const size_type wd = n / bits_per_word;
413       const word bit = word(1) << (n % bits_per_word);
414       _M_words[wd] |= bit;
415       if (wd == _M_next_word)
416 	update_next_word();
417     }
418 
419     void clear(size_type n) noexcept
420     {
421       __glibcxx_assert( n < _M_size );
422       const size_type wd = n / bits_per_word;
423       const word bit = word(1) << (n % bits_per_word);
424       _M_words[wd] &= ~bit;
425       if (wd < _M_next_word)
426 	_M_next_word = wd;
427     }
428 
429     // Update _M_next_word to refer to the next word with an unset bit.
430     // The size of the _M_next_word bit-field means it cannot represent
431     // the maximum possible nwords() value. To avoid wraparound to zero
432     // this function saturates _M_next_word at max_word_index().
433     void update_next_word() noexcept
434     {
435       size_type next = _M_next_word;
436       while (_M_words[next] == word(-1) && ++next < nwords())
437 	{ }
438       _M_next_word = std::min(next, max_word_index());
439     }
440 
441     void swap(bitset& b) noexcept
442     {
443       std::swap(_M_words, b._M_words);
444       size_type tmp = _M_size;
445       _M_size = b._M_size;
446       b._M_size = tmp;
447       tmp = _M_next_word;
448       _M_next_word = b._M_next_word;
449       b._M_next_word = tmp;
450     }
451 
452     size_type nwords() const noexcept
453     { return (_M_size + bits_per_word - 1) / bits_per_word; }
454 
455     // Maximum value that can be stored in bitset::_M_size member (approx 500k)
456     static constexpr size_type max_blocks_per_chunk() noexcept
457     { return (size_type(1) << _S_size_digits) - 1; }
458 
459     // Maximum value that can be stored in bitset::_M_next_word member (8191).
460     static constexpr size_type max_word_index() noexcept
461     { return (max_blocks_per_chunk() + bits_per_word - 1) / bits_per_word; }
462 
463     word* data() const noexcept { return _M_words; }
464 
465   private:
466     static constexpr unsigned _S_size_digits
467       = (numeric_limits<size_type>::digits
468 	  + std::__bit_width(bits_per_word) - 1) / 2;
469 
470     word* _M_words = nullptr;
471     // Number of blocks represented by the bitset:
472     size_type _M_size : _S_size_digits;
473     // Index of the first word with unset bits:
474     size_type _M_next_word : numeric_limits<size_type>::digits - _S_size_digits;
475   };
476 
477   // A "chunk" belonging to a pool.
478   // A chunk contains many blocks of the same size.
479   // Derived from bitset to reuse its tail-padding.
480   struct chunk : bitset
481   {
482     chunk() = default;
483 
484     // p points to the start of a chunk of size bytes in length.
485     // The chunk has space for n blocks, followed by a bitset of size n
486     // that begins at address words.
487     // This object does not own p or words, the caller will free it.
488     chunk(void* p, uint32_t bytes, void* words, size_t n)
489     : bitset(words, n),
490       _M_bytes(bytes),
491       _M_p(static_cast<std::byte*>(p))
492     { __glibcxx_assert(bytes <= chunk::max_bytes_per_chunk()); }
493 
494     chunk(chunk&& c) noexcept
495     : bitset(std::move(c)), _M_bytes(c._M_bytes), _M_p(c._M_p)
496     {
497       c._M_bytes = 0;
498       c._M_p = nullptr;
499       c.reset();
500     }
501 
502     chunk& operator=(chunk&& c) noexcept
503     {
504       swap(c);
505       return *this;
506     }
507 
508     // Allocated size of chunk:
509     bitset::size_type _M_bytes = 0;
510     // Start of allocated chunk:
511     std::byte* _M_p = nullptr;
512 
513     // True if there are free blocks in this chunk
514     using bitset::full;
515     // Number of blocks in this chunk
516     using bitset::size;
517 
518     static constexpr uint32_t max_bytes_per_chunk() noexcept
519     { return numeric_limits<decltype(_M_bytes)>::max(); }
520 
521     // Determine if block with address p and size block_size
522     // is contained within this chunk.
523     bool owns(void* p, size_t block_size)
524     {
525       std::less_equal<uintptr_t> less_equal;
526       return less_equal(reinterpret_cast<uintptr_t>(_M_p),
527 			reinterpret_cast<uintptr_t>(p))
528 	&& less_equal(reinterpret_cast<uintptr_t>(p) + block_size,
529 		      reinterpret_cast<uintptr_t>(bitset::data()));
530     }
531 
532     // Allocate next available block of block_size bytes from this chunk.
533     void* reserve(size_t block_size) noexcept
534     {
535       const size_type n = get_first_unset();
536       if (n == size_type(-1))
537 	return nullptr;
538       return _M_p + (n * block_size);
539     }
540 
541     // Deallocate a single block of block_size bytes
542     void release(void* vp, size_t block_size)
543     {
544       __glibcxx_assert( owns(vp, block_size) );
545       const size_t offset = static_cast<std::byte*>(vp) - _M_p;
546       // Pointer is correctly aligned for a block in this chunk:
547       __glibcxx_assert( (offset % block_size) == 0 );
548       // Block has been allocated:
549       __glibcxx_assert( (*this)[offset / block_size] == true );
550       bitset::clear(offset / block_size);
551     }
552 
553     // Deallocate a single block if it belongs to this chunk.
554     bool try_release(void* p, size_t block_size)
555     {
556       if (!owns(p, block_size))
557 	return false;
558       release(p, block_size);
559       return true;
560     }
561 
562     void swap(chunk& c) noexcept
563     {
564       std::swap(_M_bytes, c._M_bytes);
565       std::swap(_M_p, c._M_p);
566       bitset::swap(c);
567     }
568 
569     bool operator<(const chunk& c) const noexcept
570     { return std::less<const void*>{}(_M_p, c._M_p); }
571 
572     friend void swap(chunk& l, chunk& r) { l.swap(r); }
573 
574     friend bool operator<(const void* p, const chunk& c) noexcept
575     { return std::less<const void*>{}(p, c._M_p); }
576   };
577 
578   // For 64-bit pointers this is the size of three pointers i.e. 24 bytes.
579   // For 32-bit and 20-bit pointers it's four pointers (16 bytes).
580   // For 16-bit pointers it's five pointers (10 bytes).
581   // TODO pad 64-bit to 4*sizeof(void*) to avoid splitting across cache lines?
582   static_assert(sizeof(chunk)
583       == 2 * sizeof(bitset::size_type) + 2 * sizeof(void*));
584 
585   // An oversized allocation that doesn't fit in a pool.
586   struct big_block
587   {
588     // The minimum size of a big block.
589     // All big_block allocations will be a multiple of this value.
590     // Use bit_ceil to get a power of two even for e.g. 20-bit size_t.
591     static constexpr size_t min = __bit_ceil(numeric_limits<size_t>::digits);
592 
593     constexpr
594     big_block(size_t bytes, size_t alignment)
595     : _M_size(alloc_size(bytes), alignment)
596     {
597       // Check for unsigned wraparound
598       if (size() < bytes) [[unlikely]]
599 	{
600 	  // (sync|unsync)_pool_resource::do_allocate is not allowed to throw.
601 	  // If the required size is too large for size_t then ask the
602 	  // upstream resource for an impossibly large size and alignment.
603 	  _M_size.value = -1;
604 	}
605     }
606 
607     void* pointer = nullptr;
608     aligned_size<min> _M_size;
609 
610     constexpr size_t size() const noexcept
611     {
612       if (_M_size.value == size_t(-1)) [[unlikely]]
613 	return size_t(-1);
614       return _M_size.size();
615     }
616 
617     size_t align() const noexcept
618     { return _M_size.alignment(); }
619 
620     // Calculate size to be allocated instead of requested number of bytes.
621     // The requested value will be rounded up to a multiple of big_block::min,
622     // so the low bits are all zero and can be used to hold the alignment.
623     static constexpr size_t alloc_size(size_t bytes) noexcept
624     { return aligned_ceil(bytes, min); }
625 
626     friend bool operator<(void* p, const big_block& b) noexcept
627     { return less<void*>{}(p, b.pointer); }
628 
629     friend bool operator<(const big_block& b, void* p) noexcept
630     { return less<void*>{}(b.pointer, p); }
631   };
632 
633   static_assert(sizeof(big_block) == (2 * sizeof(void*)));
634 
635   } // namespace
636 
637   // A pool that serves blocks of a particular size.
638   // Each pool manages a number of chunks.
639   // When a pool is full it is replenished by allocating another chunk.
640   struct __pool_resource::_Pool
641   {
642     // Smallest supported block size
643     static constexpr unsigned _S_min_block
644       = std::max(sizeof(void*), alignof(bitset::word));
645 
646     _Pool(size_t __block_size, size_t __blocks_per_chunk)
647     : _M_chunks(),
648       _M_block_sz(__block_size),
649       _M_blocks_per_chunk(__blocks_per_chunk)
650     { }
651 
652     // Must call release(r) before destruction!
653     ~_Pool() { __glibcxx_assert(_M_chunks.empty()); }
654 
655     _Pool(_Pool&&) noexcept = default;
656     _Pool& operator=(_Pool&&) noexcept = default;
657 
658     // Size of blocks in this pool
659     size_t block_size() const noexcept
660     { return _M_block_sz; }
661 
662     // Allocate a block if the pool is not full, otherwise return null.
663     void* try_allocate() noexcept
664     {
665       const size_t blocksz = block_size();
666       if (!_M_chunks.empty())
667 	{
668 	  auto& last = _M_chunks.back();
669 	  if (void* p = last.reserve(blocksz))
670 	    return p;
671 	  // TODO last is full, so move another chunk to the back instead?
672 	  for (auto it = _M_chunks.begin(); it != &last; ++it)
673 	    if (void* p = it->reserve(blocksz))
674 	      return p;
675 	}
676       return nullptr;
677     }
678 
679     // Allocate a block from the pool, replenishing from upstream if needed.
680     void* allocate(memory_resource* r, const pool_options& opts)
681     {
682       if (void* p = try_allocate())
683 	return p;
684       replenish(r, opts);
685       return _M_chunks.back().reserve(block_size());
686     }
687 
688     // Return a block to the pool.
689     bool deallocate(memory_resource*, void* p)
690     {
691       const size_t blocksz = block_size();
692       if (__builtin_expect(!_M_chunks.empty(), true))
693 	{
694 	  auto& last = _M_chunks.back();
695 	  if (last.try_release(p, blocksz))
696 	    return true;
697 	  auto it = std::upper_bound(_M_chunks.begin(), &last, p);
698 	  if (it != _M_chunks.begin())
699 	    {
700 	      it--;
701 	      if (it->try_release(p, blocksz))
702 		// If chunk is empty could return to upstream, but we don't
703 		// currently do that. Pools only increase in size.
704 		return true;
705 	    }
706 	}
707       return false;
708     }
709 
710     void replenish(memory_resource* __r, const pool_options& __opts)
711     {
712       using word = chunk::word;
713       const size_t __blocks = _M_blocks_per_chunk;
714       const auto __bits = chunk::bits_per_word;
715       const size_t __words = (__blocks + __bits - 1) / __bits;
716       const size_t __block_size = block_size();
717       size_t __bytes = __blocks * __block_size + __words * sizeof(word);
718       size_t __alignment = std::__bit_ceil(__block_size);
719       void* __p = __r->allocate(__bytes, __alignment);
720       __try
721 	{
722 	  size_t __n = __blocks * __block_size;
723 	  void* __pwords = static_cast<char*>(__p) + __n;
724 	  _M_chunks.insert(chunk(__p, __bytes, __pwords, __blocks), __r);
725 	}
726       __catch (...)
727 	{
728 	  __r->deallocate(__p, __bytes, __alignment);
729 	}
730       if (_M_blocks_per_chunk < __opts.max_blocks_per_chunk)
731 	{
732 	  const size_t max_blocks
733 	    = (chunk::max_bytes_per_chunk() - sizeof(word))
734 	    / (__block_size + 0.125);
735 	  _M_blocks_per_chunk = std::min({
736 	      max_blocks,
737 	      __opts.max_blocks_per_chunk,
738 	      size_t(_M_blocks_per_chunk * 2)
739 	  });
740 	}
741     }
742 
743     void release(memory_resource* __r)
744     {
745       const size_t __alignment = std::__bit_ceil(block_size());
746       for (auto& __c : _M_chunks)
747 	if (__c._M_p)
748 	  __r->deallocate(__c._M_p, __c._M_bytes, __alignment);
749       _M_chunks.clear(__r);
750     }
751 
752     // A "resourceless vector" instead of pmr::vector, to save space.
753     // All resize operations need to be passed a memory resource, which
754     // obviously needs to be the same one every time.
755     // Chunks are kept sorted by address of their first block, except for
756     // the most recently-allocated Chunk which is at the end of the vector.
757     struct vector
758     {
759       using value_type = chunk;
760       using size_type = unsigned;
761       using iterator = value_type*;
762 
763       // A vector owns its data pointer but not memory held by its elements.
764       chunk* data = nullptr;
765       size_type size = 0;
766       size_type capacity = 0;
767 
768       vector() = default;
769 
770       vector(size_type __n, memory_resource* __r)
771       : data(polymorphic_allocator<value_type>(__r).allocate(__n)),
772 	capacity(__n)
773       { }
774 
775       // Must call clear(r) before destruction!
776       ~vector() { __glibcxx_assert(data == nullptr); }
777 
778       vector(vector&& __rval) noexcept
779 	: data(__rval.data), size(__rval.size), capacity(__rval.capacity)
780       {
781 	__rval.data = nullptr;
782 	__rval.capacity = __rval.size = 0;
783       }
784 
785       vector& operator=(vector&& __rval) noexcept
786       {
787 	__glibcxx_assert(data == nullptr);
788 	data = __rval.data;
789 	size = __rval.size;
790 	capacity = __rval.capacity;
791 	__rval.data = nullptr;
792 	__rval.capacity = __rval.size = 0;
793 	return *this;
794       }
795 
796       // void resize(size_type __n, memory_resource* __r);
797       // void reserve(size_type __n, memory_resource* __r);
798 
799       void clear(memory_resource* __r)
800       {
801 	if (!data)
802 	  return;
803 	// Chunks must be individually freed before clearing the vector.
804 	std::destroy(begin(), end());
805 	polymorphic_allocator<value_type>(__r).deallocate(data, capacity);
806 	data = nullptr;
807 	capacity = size = 0;
808       }
809 
810       // Sort existing elements then insert new one at the end.
811       iterator insert(chunk&& c, memory_resource* r)
812       {
813 	if (size < capacity)
814 	  {
815 	    if (size > 1)
816 	      {
817 		auto mid = end() - 1;
818 		std::rotate(std::lower_bound(begin(), mid, *mid), mid, end());
819 	      }
820 	  }
821 	else if (size > 0)
822 	  {
823 	    polymorphic_allocator<value_type> __alloc(r);
824 	    auto __mid = std::lower_bound(begin(), end() - 1, back());
825 	    auto __p = __alloc.allocate(capacity * 1.5);
826 	    // move [begin,__mid) to new storage
827 	    auto __p2 = std::move(begin(), __mid, __p);
828 	    // move end-1 to new storage
829 	    *__p2 = std::move(back());
830 	    // move [__mid,end-1) to new storage
831 	    std::move(__mid, end() - 1, ++__p2);
832 	    std::destroy(begin(), end());
833 	    __alloc.deallocate(data, capacity);
834 	    data = __p;
835 	    capacity *= 1.5;
836 	  }
837 	else
838 	  {
839 	    polymorphic_allocator<value_type> __alloc(r);
840 	    data = __alloc.allocate(capacity = 8);
841 	  }
842 	auto back = ::new (data + size) chunk(std::move(c));
843 	__glibcxx_assert(std::is_sorted(begin(), back));
844 	++size;
845 	return back;
846       }
847 
848       iterator begin() const { return data; }
849       iterator end() const { return data + size; }
850 
851       bool empty() const noexcept { return size == 0; }
852 
853       value_type& back() { return data[size - 1]; }
854     };
855 
856     vector _M_chunks;
857     unsigned _M_block_sz; 	// size of blocks allocated from this pool
858     unsigned _M_blocks_per_chunk;	// number of blocks to allocate next
859   };
860 
861   // An oversized allocation that doesn't fit in a pool.
862   struct __pool_resource::_BigBlock : big_block
863   {
864     using big_block::big_block;
865   };
866 
867   namespace {
868 
869   constexpr size_t pool_sizes[] = {
870       8, 16, 24,
871       32, 48,
872       64, 80, 96, 112,
873       128, 192,
874       256, 320, 384, 448,
875       512, 768,
876 #if __SIZE_WIDTH__ > 16
877       // Use bigger pools if size_t has at least 20 bits.
878       1024, 1536,
879       2048, 3072,
880 #if __INT_WIDTH__ >= 32
881       // Use even bigger pools if int has at least 32 bits.
882       1<<12, 1<<13, 1<<14,
883       1<<15, 1<<16, 1<<17,
884       1<<20, 1<<21, 1<<22 // 4MB should be enough for anybody
885 #endif
886 #endif
887   };
888 
889   pool_options
890   munge_options(pool_options opts)
891   {
892     // The values in the returned struct may differ from those supplied
893     // to the pool resource constructor in that values of zero will be
894     // replaced with implementation-defined defaults, and sizes may be
895     // rounded to unspecified granularity.
896 
897     // max_blocks_per_chunk sets the absolute maximum for the pool resource.
898     // Each pool might have a smaller maximum, because pools for very large
899     // objects might impose  smaller limit.
900     if (opts.max_blocks_per_chunk == 0)
901       {
902 	// Pick a default that depends on the number of bits in size_t.
903 	opts.max_blocks_per_chunk = __SIZE_WIDTH__ << 8;
904       }
905     else
906       {
907 	// Round to preferred granularity.
908 	if (opts.max_blocks_per_chunk < size_t(-4))
909 	  {
910 	    // round up
911 	    opts.max_blocks_per_chunk
912 	      = aligned_ceil(opts.max_blocks_per_chunk, 4);
913 	  }
914 	else
915 	  {
916 	    // round down
917 	    opts.max_blocks_per_chunk &= ~size_t(3);
918 	  }
919       }
920 
921     if (opts.max_blocks_per_chunk > chunk::max_blocks_per_chunk())
922       {
923 	opts.max_blocks_per_chunk = chunk::max_blocks_per_chunk();
924       }
925 
926     // largest_required_pool_block specifies the largest block size that will
927     // be allocated from a pool. Larger allocations will come directly from
928     // the upstream resource and so will not be pooled.
929     if (opts.largest_required_pool_block == 0)
930       {
931 	// Pick a sensible default that depends on the number of bits in size_t
932 	// (pools with larger block sizes must be explicitly requested by
933 	// using a non-zero value for largest_required_pool_block).
934 	opts.largest_required_pool_block = __SIZE_WIDTH__ << 6;
935       }
936     else
937       {
938 	// Round to preferred granularity
939 	static_assert(std::__has_single_bit(pool_sizes[0]));
940 	opts.largest_required_pool_block
941 	  = aligned_ceil(opts.largest_required_pool_block, pool_sizes[0]);
942       }
943 
944     if (opts.largest_required_pool_block < big_block::min)
945       {
946 	opts.largest_required_pool_block = big_block::min;
947       }
948     else if (opts.largest_required_pool_block > std::end(pool_sizes)[-1])
949       {
950 	// Setting _M_opts to the largest pool allows users to query it:
951 	opts.largest_required_pool_block = std::end(pool_sizes)[-1];
952       }
953     return opts;
954   }
955 
956   inline int
957   pool_index(size_t block_size, int npools)
958   {
959     auto p = std::lower_bound(pool_sizes, pool_sizes + npools, block_size);
960     int n = p - pool_sizes;
961     if (n != npools)
962       return n;
963     return -1;
964   }
965 
966   inline int
967   select_num_pools(const pool_options& opts)
968   {
969     auto p = std::lower_bound(std::begin(pool_sizes), std::end(pool_sizes),
970 			      opts.largest_required_pool_block);
971     const int n = p - std::begin(pool_sizes);
972     if (p == std::end(pool_sizes))
973       return n;
974     return n + 1;
975   }
976 
977 #ifdef _GLIBCXX_HAS_GTHREADS
978   using shared_lock = std::shared_lock<shared_mutex>;
979   using exclusive_lock = lock_guard<shared_mutex>;
980 #endif
981 
982   } // namespace
983 
984   __pool_resource::
985   __pool_resource(const pool_options& opts, memory_resource* upstream)
986   : _M_opts(munge_options(opts)), _M_unpooled(upstream),
987     _M_npools(select_num_pools(_M_opts))
988   { }
989 
990   __pool_resource::~__pool_resource() { release(); }
991 
992   void
993   __pool_resource::release() noexcept
994   {
995     memory_resource* res = resource();
996     // deallocate oversize allocations
997     for (auto& b : _M_unpooled)
998       res->deallocate(b.pointer, b.size(), b.align());
999     pmr::vector<_BigBlock>{res}.swap(_M_unpooled);
1000   }
1001 
1002   void*
1003   __pool_resource::allocate(size_t bytes, size_t alignment)
1004   {
1005     auto& b = _M_unpooled.emplace_back(bytes, alignment);
1006     __try {
1007       // N.B. need to allocate b.size(), which might be larger than bytes.
1008       // Also use b.align() instead of alignment parameter, which will be
1009       // an impossibly large value if (bytes+bookkeeping) > SIZE_MAX.
1010       void* p = resource()->allocate(b.size(), b.align());
1011       b.pointer = p;
1012       if (_M_unpooled.size() > 1)
1013 	{
1014 	  const auto mid = _M_unpooled.end() - 1;
1015 	  // move to right position in vector
1016 	  std::rotate(std::lower_bound(_M_unpooled.begin(), mid, p),
1017 		      mid, _M_unpooled.end());
1018 	}
1019       return p;
1020     } __catch(...) {
1021       _M_unpooled.pop_back();
1022       __throw_exception_again;
1023     }
1024   }
1025 
1026   void
1027   __pool_resource::deallocate(void* p, size_t bytes [[maybe_unused]],
1028 			      size_t alignment [[maybe_unused]])
1029   {
1030     const auto it
1031       = std::lower_bound(_M_unpooled.begin(), _M_unpooled.end(), p);
1032     __glibcxx_assert(it != _M_unpooled.end() && it->pointer == p);
1033     if (it != _M_unpooled.end() && it->pointer == p) // [[likely]]
1034       {
1035 	const auto b = *it;
1036 	__glibcxx_assert(b.size() == b.alloc_size(bytes));
1037 	__glibcxx_assert(b.align() == alignment);
1038 	_M_unpooled.erase(it);
1039 	// N.B. need to deallocate b.size(), which might be larger than bytes.
1040 	resource()->deallocate(p, b.size(), b.align());
1041       }
1042   }
1043 
1044   // Create array of pools, allocated from upstream resource.
1045   auto
1046   __pool_resource::_M_alloc_pools()
1047   -> _Pool*
1048   {
1049     polymorphic_allocator<_Pool> alloc{resource()};
1050     _Pool* p = alloc.allocate(_M_npools);
1051     for (int i = 0; i < _M_npools; ++i)
1052       {
1053 	// For last pool use largest_required_pool_block
1054 	const size_t block_size = (i+1 == _M_npools)
1055 	  ? _M_opts.largest_required_pool_block
1056 	  : pool_sizes[i];
1057 
1058 	// Decide on initial number of blocks per chunk.
1059 	// At least 16 blocks per chunk seems reasonable,
1060 	// more for smaller blocks:
1061 	size_t blocks_per_chunk = 1024 / block_size;
1062 	blocks_per_chunk = std::max(size_t(16), blocks_per_chunk);
1063 	// But don't exceed the requested max_blocks_per_chunk:
1064 	blocks_per_chunk
1065 	  = std::min(blocks_per_chunk, _M_opts.max_blocks_per_chunk);
1066 	// Allow space for bitset to track which blocks are used/unused:
1067 	blocks_per_chunk *= 1 - 1.0 / (__CHAR_BIT__ * block_size);
1068 	// Construct a _Pool for the given block size and initial chunk size:
1069 	alloc.construct(p + i, block_size, blocks_per_chunk);
1070       }
1071     return p;
1072   }
1073 
1074 #ifdef _GLIBCXX_HAS_GTHREADS
1075   // synchronized_pool_resource members.
1076 
1077   /* Notes on implementation and thread safety:
1078    *
1079    * Each synchronized_pool_resource manages an linked list of N+1 _TPools
1080    * objects, where N is the number of threads using the pool resource.
1081    * Each _TPools object has its own set of pools, with their own chunks.
1082    * The first element of the list, _M_tpools[0], can be used by any thread.
1083    * The rest of the list contains a _TPools object for each thread,
1084    * accessed via the thread-specific key _M_key (and referred to for
1085    * exposition as _M_tpools[_M_key]).
1086    * The first element, _M_tpools[0], contains "orphaned chunks" which were
1087    * allocated by a thread which has since exited, and so there is no
1088    * _M_tpools[_M_key] for that thread. Orphaned chunks are never reused,
1089    * they're only held in _M_tpools[0] so they can be deallocated.
1090    * A thread can access its own thread-specific set of pools via _M_key
1091    * while holding a shared lock on _M_mx. Accessing _M_impl._M_unpooled
1092    * or _M_tpools[0] or any other thread's _M_tpools[_M_key] requires an
1093    * exclusive lock.
1094    * The upstream_resource() pointer can be obtained without a lock, but
1095    * any dereference of that pointer requires an exclusive lock.
1096    * The _M_impl._M_opts and _M_impl._M_npools members are immutable,
1097    * and can safely be accessed concurrently.
1098    *
1099    * In a single-threaded program (i.e. __gthread_active_p() == false)
1100    * the pool resource only needs one set of pools and never has orphaned
1101    * chunks, so just uses _M_tpools[0] directly, and _M_tpools->next is null.
1102    */
1103 
1104   extern "C" {
1105     static void destroy_TPools(void*);
1106   }
1107 
1108   struct synchronized_pool_resource::_TPools
1109   {
1110     // Exclusive lock must be held in the thread where this constructor runs.
1111     explicit
1112     _TPools(synchronized_pool_resource& owner, exclusive_lock&)
1113     : owner(owner), pools(owner._M_impl._M_alloc_pools())
1114     {
1115       // __builtin_printf("%p constructing\n", this);
1116       __glibcxx_assert(pools);
1117     }
1118 
1119     // Exclusive lock must be held in the thread where this destructor runs.
1120     ~_TPools()
1121     {
1122       __glibcxx_assert(pools);
1123       if (pools)
1124 	{
1125 	  memory_resource* r = owner.upstream_resource();
1126 	  for (int i = 0; i < owner._M_impl._M_npools; ++i)
1127 	    pools[i].release(r);
1128 	  std::destroy_n(pools, owner._M_impl._M_npools);
1129 	  polymorphic_allocator<__pool_resource::_Pool> a(r);
1130 	  a.deallocate(pools, owner._M_impl._M_npools);
1131 	}
1132       if (prev)
1133 	prev->next = next;
1134       if (next)
1135 	next->prev = prev;
1136     }
1137 
1138     // Exclusive lock must be held in the thread where this function runs.
1139     void move_nonempty_chunks()
1140     {
1141       __glibcxx_assert(pools);
1142       __glibcxx_assert(__gthread_active_p());
1143       if (!pools)
1144 	return;
1145       memory_resource* const r = owner.upstream_resource();
1146       auto* const shared = owner._M_tpools->pools;
1147       // move all non-empty chunks to the shared _TPools
1148       for (int i = 0; i < owner._M_impl._M_npools; ++i)
1149 	for (auto& c : pools[i]._M_chunks)
1150 	  if (!c.empty())
1151 	    shared[i]._M_chunks.insert(std::move(c), r);
1152     }
1153 
1154     synchronized_pool_resource& owner;
1155     __pool_resource::_Pool* pools = nullptr;
1156     _TPools* prev = nullptr;
1157     _TPools* next = nullptr;
1158 
1159     static void destroy(_TPools* p)
1160     {
1161       exclusive_lock l(p->owner._M_mx);
1162       // __glibcxx_assert(p != p->owner._M_tpools);
1163       p->move_nonempty_chunks();
1164       polymorphic_allocator<_TPools> a(p->owner.upstream_resource());
1165       p->~_TPools();
1166       a.deallocate(p, 1);
1167     }
1168   };
1169 
1170   // Called when a thread exits
1171   extern "C" {
1172     static void destroy_TPools(void* p)
1173     {
1174       using _TPools = synchronized_pool_resource::_TPools;
1175       _TPools::destroy(static_cast<_TPools*>(p));
1176     }
1177   }
1178 
1179   // Constructor
1180   synchronized_pool_resource::
1181   synchronized_pool_resource(const pool_options& opts,
1182 			     memory_resource* upstream)
1183   : _M_impl(opts, upstream)
1184   {
1185     if (__gthread_active_p())
1186       if (int err = __gthread_key_create(&_M_key, destroy_TPools))
1187 	__throw_system_error(err);
1188     exclusive_lock l(_M_mx);
1189     _M_tpools = _M_alloc_shared_tpools(l);
1190   }
1191 
1192   // Destructor
1193   synchronized_pool_resource::~synchronized_pool_resource()
1194   {
1195     release();
1196     if (__gthread_active_p())
1197       __gthread_key_delete(_M_key); // does not run destroy_TPools
1198   }
1199 
1200   void
1201   synchronized_pool_resource::release()
1202   {
1203     exclusive_lock l(_M_mx);
1204     if (_M_tpools)
1205       {
1206 	if (__gthread_active_p())
1207 	  {
1208 	    __gthread_key_delete(_M_key); // does not run destroy_TPools
1209 	    __gthread_key_create(&_M_key, destroy_TPools);
1210 	  }
1211 	polymorphic_allocator<_TPools> a(upstream_resource());
1212 	// destroy+deallocate each _TPools
1213 	do
1214 	  {
1215 	    _TPools* p = _M_tpools;
1216 	    _M_tpools = _M_tpools->next;
1217 	    p->~_TPools();
1218 	    a.deallocate(p, 1);
1219 	  }
1220 	while (_M_tpools);
1221       }
1222     // release unpooled memory
1223     _M_impl.release();
1224   }
1225 
1226   // Caller must hold shared or exclusive lock to ensure the pointer
1227   // isn't invalidated before it can be used.
1228   auto
1229   synchronized_pool_resource::_M_thread_specific_pools() noexcept
1230   {
1231     __pool_resource::_Pool* pools = nullptr;
1232     __glibcxx_assert(__gthread_active_p());
1233     if (auto tp = static_cast<_TPools*>(__gthread_getspecific(_M_key)))
1234       {
1235 	pools = tp->pools;
1236 	// __glibcxx_assert(tp->pools);
1237       }
1238     return pools;
1239   }
1240 
1241   // Override for memory_resource::do_allocate
1242   void*
1243   synchronized_pool_resource::
1244   do_allocate(size_t bytes, size_t alignment)
1245   {
1246     const auto block_size = std::max(bytes, alignment);
1247     const pool_options opts = _M_impl._M_opts;
1248     if (block_size <= opts.largest_required_pool_block)
1249       {
1250 	const ptrdiff_t index = pool_index(block_size, _M_impl._M_npools);
1251 	if (__gthread_active_p())
1252 	  {
1253 	    // Try to allocate from the thread-specific pool.
1254 	    shared_lock l(_M_mx);
1255 	    if (auto pools = _M_thread_specific_pools()) // [[likely]]
1256 	      {
1257 		// Need exclusive lock to replenish so use try_allocate:
1258 		if (void* p = pools[index].try_allocate())
1259 		  return p;
1260 		// Need to take exclusive lock and replenish pool.
1261 	      }
1262 	    // Need to allocate or replenish thread-specific pools using
1263 	    // upstream resource, so need to hold exclusive lock.
1264 	  }
1265 	else // single-threaded
1266 	  {
1267 	    if (!_M_tpools) // [[unlikely]]
1268 	      {
1269 		exclusive_lock dummy(_M_mx);
1270 		_M_tpools = _M_alloc_shared_tpools(dummy);
1271 	      }
1272 	    return _M_tpools->pools[index].allocate(upstream_resource(), opts);
1273 	  }
1274 
1275 	// N.B. Another thread could call release() now lock is not held.
1276 	exclusive_lock excl(_M_mx);
1277 	if (!_M_tpools) // [[unlikely]]
1278 	  _M_tpools = _M_alloc_shared_tpools(excl);
1279 	auto pools = _M_thread_specific_pools();
1280 	if (!pools)
1281 	  pools = _M_alloc_tpools(excl)->pools;
1282 	return pools[index].allocate(upstream_resource(), opts);
1283       }
1284     exclusive_lock l(_M_mx);
1285     return _M_impl.allocate(bytes, alignment); // unpooled allocation
1286   }
1287 
1288   // Override for memory_resource::do_deallocate
1289   void
1290   synchronized_pool_resource::
1291   do_deallocate(void* p, size_t bytes, size_t alignment)
1292   {
1293     size_t block_size = std::max(bytes, alignment);
1294     if (block_size <= _M_impl._M_opts.largest_required_pool_block)
1295       {
1296 	const ptrdiff_t index = pool_index(block_size, _M_impl._M_npools);
1297 	__glibcxx_assert(index != -1);
1298 	if (__gthread_active_p())
1299 	  {
1300 	    shared_lock l(_M_mx);
1301 	    if (auto pools = _M_thread_specific_pools())
1302 	      {
1303 		// No need to lock here, no other thread is accessing this pool.
1304 		if (pools[index].deallocate(upstream_resource(), p))
1305 		  return;
1306 	      }
1307 	    // Block might have come from a different thread's pool,
1308 	    // take exclusive lock and check every pool.
1309 	  }
1310 	else // single-threaded
1311 	  {
1312 	    __glibcxx_assert(_M_tpools != nullptr);
1313 	    if (_M_tpools) // [[likely]]
1314 	      _M_tpools->pools[index].deallocate(upstream_resource(), p);
1315 	    return;
1316 	  }
1317 
1318 	// TODO store {p, bytes, alignment} somewhere and defer returning
1319 	// the block to the correct thread-specific pool until we next
1320 	// take the exclusive lock.
1321 
1322 	exclusive_lock excl(_M_mx);
1323 	auto my_pools = _M_thread_specific_pools();
1324 	for (_TPools* t = _M_tpools; t != nullptr; t = t->next)
1325 	  {
1326 	    if (t->pools != my_pools)
1327 	      if (t->pools) // [[likely]]
1328 		{
1329 		  if (t->pools[index].deallocate(upstream_resource(), p))
1330 		    return;
1331 		}
1332 	  }
1333 	// Not necessarily an error to reach here, release() could have been
1334 	// called on another thread between releasing the shared lock and
1335 	// acquiring the exclusive lock.
1336 	return;
1337       }
1338     exclusive_lock l(_M_mx);
1339     _M_impl.deallocate(p, bytes, alignment);
1340   }
1341 
1342   // Allocate a thread-specific _TPools object and add it to the linked list.
1343   auto
1344   synchronized_pool_resource::_M_alloc_tpools(exclusive_lock& l)
1345   -> _TPools*
1346   {
1347     __glibcxx_assert(_M_tpools != nullptr);
1348     __glibcxx_assert(__gthread_active_p());
1349     // dump_list(_M_tpools);
1350     polymorphic_allocator<_TPools> a(upstream_resource());
1351     _TPools* p = a.allocate(1);
1352     bool constructed = false;
1353     __try
1354       {
1355 	a.construct(p, *this, l);
1356 	constructed = true;
1357 	// __glibcxx_assert(__gthread_getspecific(_M_key) == nullptr);
1358 	if (int err = __gthread_setspecific(_M_key, p))
1359 	  __throw_system_error(err);
1360       }
1361     __catch(...)
1362       {
1363 	if (constructed)
1364 	  a.destroy(p);
1365 	a.deallocate(p, 1);
1366 	__throw_exception_again;
1367       }
1368     p->prev = _M_tpools;
1369     p->next = _M_tpools->next;
1370     _M_tpools->next = p;
1371     if (p->next)
1372       p->next->prev = p;
1373     return p;
1374   }
1375 
1376   // Allocate the shared _TPools object, _M_tpools[0]
1377   auto
1378   synchronized_pool_resource::_M_alloc_shared_tpools(exclusive_lock& l)
1379   -> _TPools*
1380   {
1381     __glibcxx_assert(_M_tpools == nullptr);
1382     polymorphic_allocator<_TPools> a(upstream_resource());
1383     _TPools* p = a.allocate(1);
1384     __try
1385       {
1386 	a.construct(p, *this, l);
1387       }
1388     __catch(...)
1389       {
1390 	a.deallocate(p, 1);
1391 	__throw_exception_again;
1392       }
1393     // __glibcxx_assert(p->next == nullptr);
1394     // __glibcxx_assert(p->prev == nullptr);
1395     return p;
1396   }
1397 #endif // _GLIBCXX_HAS_GTHREADS
1398 
1399   // unsynchronized_pool_resource member functions
1400 
1401   // Constructor
1402   unsynchronized_pool_resource::
1403   unsynchronized_pool_resource(const pool_options& opts,
1404 			       memory_resource* upstream)
1405   : _M_impl(opts, upstream), _M_pools(_M_impl._M_alloc_pools())
1406   { }
1407 
1408   // Destructor
1409   unsynchronized_pool_resource::~unsynchronized_pool_resource()
1410   { release(); }
1411 
1412   // Return all memory to upstream resource.
1413   void
1414   unsynchronized_pool_resource::release()
1415   {
1416     // release pooled memory
1417     if (_M_pools)
1418       {
1419 	memory_resource* res = upstream_resource();
1420 	polymorphic_allocator<_Pool> alloc{res};
1421 	for (int i = 0; i < _M_impl._M_npools; ++i)
1422 	  {
1423 	    _M_pools[i].release(res);
1424 	    alloc.destroy(_M_pools + i);
1425 	  }
1426 	alloc.deallocate(_M_pools, _M_impl._M_npools);
1427 	_M_pools = nullptr;
1428       }
1429 
1430     // release unpooled memory
1431     _M_impl.release();
1432   }
1433 
1434   // Find the right pool for a block of size block_size.
1435   auto
1436   unsynchronized_pool_resource::_M_find_pool(size_t block_size) noexcept
1437   {
1438     __pool_resource::_Pool* pool = nullptr;
1439     if (_M_pools) // [[likely]]
1440       {
1441 	int index = pool_index(block_size, _M_impl._M_npools);
1442 	if (index != -1)
1443 	  pool = _M_pools + index;
1444       }
1445     return pool;
1446   }
1447 
1448   // Override for memory_resource::do_allocate
1449   void*
1450   unsynchronized_pool_resource::do_allocate(size_t bytes, size_t alignment)
1451   {
1452     const auto block_size = std::max(bytes, alignment);
1453     if (block_size <= _M_impl._M_opts.largest_required_pool_block)
1454       {
1455 	// Recreate pools if release() has been called:
1456 	if (__builtin_expect(_M_pools == nullptr, false))
1457 	  _M_pools = _M_impl._M_alloc_pools();
1458 	if (auto pool = _M_find_pool(block_size))
1459 	  return pool->allocate(upstream_resource(), _M_impl._M_opts);
1460       }
1461     return _M_impl.allocate(bytes, alignment);
1462   }
1463 
1464   // Override for memory_resource::do_deallocate
1465   void
1466   unsynchronized_pool_resource::
1467   do_deallocate(void* p, size_t bytes, size_t alignment)
1468   {
1469     size_t block_size = std::max(bytes, alignment);
1470     if (block_size <= _M_impl._M_opts.largest_required_pool_block)
1471       {
1472 	if (auto pool = _M_find_pool(block_size))
1473 	  {
1474 	    pool->deallocate(upstream_resource(), p);
1475 	    return;
1476 	  }
1477       }
1478     _M_impl.deallocate(p, bytes, alignment);
1479   }
1480 
1481 } // namespace pmr
1482 _GLIBCXX_END_NAMESPACE_VERSION
1483 } // namespace std
1484