libcoro  1.0
Coroutine support library for C++20
stackful.h
1 #pragma once
2 #include <algorithm>
3 #include <cstddef>
4 #include <memory>
5 #include <vector>
6 
7 namespace coro {
8 
9 
10 namespace _details {
11 
12 template<std::size_t segment_size>
13 class stackful_allocator;
14 }
15 
17 
40 template<std::size_t segment_size=8192>
41 class stackful {
42 public:
43 
44  stackful():_control(std::make_shared<_details::stackful_allocator<segment_size> >()) {}
45 
46  void *alloc(std::size_t sz) {
47  return _control->alloc(_control, sz);
48  }
49  static void dealloc(void *ptr, std::size_t sz) {
50  _details::stackful_allocator<segment_size>::dealloc(ptr, sz);
51  }
52 
53  std::size_t get_alloc_size() const {
54  return _control-> get_alloc_size();
55  }
56  std::size_t get_reserved() const {
57  return _control-> get_reserved();
58  }
59  std::size_t get_segment_count() const {
60  return _control-> get_segment_count();
61  }
62  std::size_t get_pending_deallocation_count() const {
63  return _control->get_pending_deallocation_count();
64  }
65 
66 protected:
67  std::shared_ptr<_details::stackful_allocator<segment_size> > _control;
68 };
69 
70 
71 namespace _details {
72 
73 template<std::size_t segment_size>
74 class stackful_allocator {
75 public:
76 
77 
78  stackful_allocator():_top_segment(0) {
79  _segments.push_back({
80  std::make_unique<segment>()
81  });
82  }
83 
84  struct segment {
85  char space[segment_size];
86  };
87 
88  struct segment_meta {
89  std::unique_ptr<segment> _segment;
90  unsigned int _usage = 0;
91  };
92 
93  static void *alloc(const std::shared_ptr<stackful_allocator> &ptr, std::size_t sz) {
94  auto me = ptr.get();
95  if (me->_top_segment == 0 && me->_segments[0]._usage == 0) {
96  me->_hold = ptr;
97  }
98  return me->do_alloc(sz);
99  }
100 
101  void *do_alloc(std::size_t sz) {
102  std::size_t need = sz + sizeof(void *);
103 
104  if (need > segment_size) {
105  void *ptr = ::operator new(need);
106  auto ref = reinterpret_cast<stackful_allocator **>(reinterpret_cast<char *>(ptr)+sz);
107  *ref= nullptr;
108  return ptr;
109  }
110 
111  segment_meta &top = _segments[_top_segment];
112  auto remain = segment_size - top._usage;
113  if (need > remain) {
114  ++_top_segment;
115  if (_top_segment >= _segments.size()) {
116  _segments.push_back({std::make_unique<segment>()});
117  }
118  return do_alloc(sz);
119  }
120  void *ptr = top._segment->space + top._usage;
121  top._usage += static_cast<unsigned int>(need);
122  auto ref = reinterpret_cast<stackful_allocator **>(reinterpret_cast<char *>(ptr)+sz);
123  *ref= this;
124  return ptr;
125  }
126 
127  static void dealloc(void *ptr, std::size_t sz) noexcept {
128  auto ref = reinterpret_cast<stackful_allocator **>(reinterpret_cast<char *>(ptr)+sz);
129  if (*ref == nullptr) {
130  ::operator delete(ptr);
131  return;
132  }
133  (*ref)->do_dealloc(ptr,sz);
134  }
135 
136  void do_dealloc(void *ptr, std::size_t sz) {
137  bool rep = do_dealloc2(ptr, sz);
138  if (rep) {
139  do {
140  auto iter =std::remove_if(_pending.begin(), _pending.end(), [&](const auto &p){
141  return do_dealloc2(p.first, p.second);
142  });
143  rep = iter != _pending.end();
144  if (rep) {
145  _pending.erase(iter, _pending.end());
146  }
147  } while (rep);
148  } else {
149  _pending.push_back({ptr,sz});
150  }
151 
152  }
153 
154  bool do_dealloc2(void *ptr, std::size_t sz) {
155  std::size_t need = sz + sizeof(void *);
156  segment_meta &top = _segments[_top_segment];
157  if (top._usage < sz) throw std::runtime_error("[coro::stackful fatal] Stack is corrupted: (size > usage)");
158  void *ref_ptr = top._segment->space + top._usage - need;
159  if (ref_ptr != ptr) return false;
160  top._usage -= static_cast<unsigned int>(need);
161  if (top._usage == 0) {
162  if (_top_segment == 0) {
163  _hold.reset();
164  } else {
165  --_top_segment;
166  }
167  }
168  return true;
169  }
170 
171  std::size_t get_alloc_size() const {
172  std::size_t s = 0;
173  for (const auto &x: _segments) s += x._usage;
174  return s;
175  }
176  std::size_t get_reserved() const {
177  return _segments.size() * segment_size;
178  }
179  std::size_t get_segment_count() const {
180  return _segments.size();
181  }
182  std::size_t get_pending_deallocation_count() const {
183  return _pending.size();
184  }
185 
186 
187 protected:
188  std::shared_ptr<stackful_allocator> _hold;
189  std::vector<segment_meta> _segments;
190  std::vector<std::pair<void *, std::size_t> > _pending;
191  std::size_t _top_segment = 0;
192 
193 };
194 
195 }
196 
197 }
Coroutine allocator emulates coroutine's stack to achieve stackful behaviour.
Definition: stackful.h:41
main namespace
Definition: aggregator.h:8