libcoro  1.0
Coroutine support library for C++20
pool_alloc.h
1 #pragma once
2 
3 #include "condition.h"
4 
5 #include <stdexcept>
6 namespace coro {
7 
8 namespace _details {
9 
10  class pool_control;
11 
12  struct pool_block {
13  union {
14  pool_control *_control;
15  pool_block *_next;
16  };
17  std::size_t _size = 0;
18  void *get_ptr() {
19  return this+1;
20  }
21 
22  static pool_block *alloc(std::size_t sz) {
23  auto whole_size = sz + sizeof(pool_block);
24  pool_block *b = reinterpret_cast<pool_block *>(::operator new(whole_size));
25  b->_size = sz;
26  return b;
27  }
28 
29  static void dealloc(pool_block *blk) {
30  ::operator delete(blk);
31  }
32 
33  static pool_block *from_ptr(void *ptr) {
34  auto me = reinterpret_cast<pool_block *>(ptr);
35  return me-1;
36  }
37  };
38 
39 
40  class pool_control {
41  public:
42  static thread_local pool_control instance;
43 
44  pool_control() = default;
45  pool_control(const pool_control &) = delete;
46  pool_control &operator=(const pool_control &) = delete;
47 
48  ~pool_control() {
49  clean_all();
50  }
51 
52 
53  pool_block *pick(std::size_t sz) {
54  pool_block *out;
55  do {
56  if (_hashtable.empty()) {
57  if (!process_dropped()) {
58  out = pool_block::alloc(sz);
59  break;
60  }
61  }
62  do {
63  pool_block *& b = map_size(sz);
64  pool_block **lnk = &b;
65  pool_block *p = b;
66  while (p && p->_size != sz) {
67  lnk = &p->_next;
68  p = *lnk;
69  }
70  if (p) {
71  *lnk = p->_next;
72  if (!b) --_keys;
73  p->_control = this;
74  return p;
75  }
76  } while (process_dropped());
77  out = pool_block::alloc(sz);
78  } while (false);
79  out->_control = this;
80  return out;
81  }
82 
83  pool_block *& map_size(std::size_t sz) {
84  auto idx = sz % _hashtable.size();
85  return _hashtable[idx];
86  }
87 
88  void insert(pool_block *blk) {
89  check_resize();
90  auto &b = map_size(blk->_size);
91  if (!b) ++_keys;
92  blk->_next = b;
93  b = blk;
94  }
95 
96  bool process_dropped() {
97  pool_block *lst = _dropped.exchange(nullptr, std::memory_order_relaxed);
98  if (!lst) return false;
99  while (lst) {
100  auto p = lst;
101  lst = lst->_next;
102  insert(p);
103  }
104  return true;
105  }
106 
107  void drop(pool_block *blk) {
108  blk->_next = _dropped.load(std::memory_order_relaxed);
109  while (!_dropped.compare_exchange_weak(blk->_next, blk,std::memory_order_relaxed));
110  }
111 
112  void clean_all() {
113  pool_block *lst = _dropped.exchange(nullptr, std::memory_order_relaxed);
114  while (lst) {
115  auto p = lst;
116  lst = lst->_next;
117  p->dealloc(p);
118  }
119  for (auto &x: _hashtable) {
120  while (x) {
121  auto p = x;
122  x = x->_next;
123  p->dealloc(p);
124  }
125  }
126  }
127 
128  void check_resize() {
129  auto sz =_hashtable.size();
130  if (_keys * 2 >= sz) {
131  std::size_t newsz = next_prime_twice_than(std::max<std::size_t>(sz, 16));
132  std::vector<pool_block *> tmp(newsz, nullptr);
133  std::swap(tmp, _hashtable);
134  _keys = 0;
135  for (auto &b :tmp) {
136  while (b) {
137  auto x = b;
138  b = b->_next;
139  insert(x);
140  }
141  }
142  }
143  }
144 
145  private:
146  std::vector<pool_block *> _hashtable;
147  std::atomic<pool_block *> _dropped = {};
148  std::size_t _keys = 0;
149  };
150 
151 
152  inline thread_local pool_control pool_control::instance;
153 
154 }
155 
157 
171 class pool_alloc {
172 public:
173 
175 
179  static void *alloc(std::size_t sz) {
180  sz = (sz + 0xF) & ~0xF;
181  auto blk = _details::pool_control::instance.pick(sz);
182  return blk->get_ptr();
183  }
184 
186 
190  static void dealloc(void *ptr, std::size_t sz) {
191  sz = (sz + 0xF) & ~0xF;
192  auto blk = _details::pool_block::from_ptr(ptr);
193  if (blk->_size != sz) throw std::runtime_error("coro::pool_alloc - invalid pointer for dealloc()");
194  auto control = blk->_control;
195  if (control != &_details::pool_control::instance) {
196  control->drop(blk);
197  } else {
198  control->insert(blk);
199  }
200  }
201 };
202 
203 
204 
205 }
static void * alloc(std::size_t sz)
allocate a block
Definition: pool_alloc.h:179
static void dealloc(void *ptr, std::size_t sz)
deallocate block
Definition: pool_alloc.h:190
A corutine allocator that caches unused frames in the pool.
Definition: pool_alloc.h:171
main namespace
Definition: aggregator.h:8