12 template<std::
size_t segment_size>
13 class stackful_allocator;
40 template<std::
size_t segment_size=8192>
44 stackful():_control(std::make_shared<_details::stackful_allocator<segment_size> >()) {}
46 void *alloc(std::size_t sz) {
47 return _control->alloc(_control, sz);
49 static void dealloc(
void *ptr, std::size_t sz) {
50 _details::stackful_allocator<segment_size>::dealloc(ptr, sz);
53 std::size_t get_alloc_size()
const {
54 return _control-> get_alloc_size();
56 std::size_t get_reserved()
const {
57 return _control-> get_reserved();
59 std::size_t get_segment_count()
const {
60 return _control-> get_segment_count();
62 std::size_t get_pending_deallocation_count()
const {
63 return _control->get_pending_deallocation_count();
67 std::shared_ptr<_details::stackful_allocator<segment_size> > _control;
73 template<std::
size_t segment_size>
74 class stackful_allocator {
78 stackful_allocator():_top_segment(0) {
80 std::make_unique<segment>()
85 char space[segment_size];
89 std::unique_ptr<segment> _segment;
90 unsigned int _usage = 0;
93 static void *alloc(
const std::shared_ptr<stackful_allocator> &ptr, std::size_t sz) {
95 if (me->_top_segment == 0 && me->_segments[0]._usage == 0) {
98 return me->do_alloc(sz);
101 void *do_alloc(std::size_t sz) {
102 std::size_t need = sz +
sizeof(
void *);
104 if (need > segment_size) {
105 void *ptr = ::operator
new(need);
106 auto ref =
reinterpret_cast<stackful_allocator **
>(
reinterpret_cast<char *
>(ptr)+sz);
111 segment_meta &top = _segments[_top_segment];
112 auto remain = segment_size - top._usage;
115 if (_top_segment >= _segments.size()) {
116 _segments.push_back({std::make_unique<segment>()});
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);
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);
133 (*ref)->do_dealloc(ptr,sz);
136 void do_dealloc(
void *ptr, std::size_t sz) {
137 bool rep = do_dealloc2(ptr, sz);
140 auto iter =std::remove_if(_pending.begin(), _pending.end(), [&](
const auto &p){
141 return do_dealloc2(p.first, p.second);
143 rep = iter != _pending.end();
145 _pending.erase(iter, _pending.end());
149 _pending.push_back({ptr,sz});
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) {
171 std::size_t get_alloc_size()
const {
173 for (
const auto &x: _segments) s += x._usage;
176 std::size_t get_reserved()
const {
177 return _segments.size() * segment_size;
179 std::size_t get_segment_count()
const {
180 return _segments.size();
182 std::size_t get_pending_deallocation_count()
const {
183 return _pending.size();
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;
Coroutine allocator emulates coroutine's stack to achieve stackful behaviour.