libcoro  1.0
Coroutine support library for C++20
allocator.h
1 #pragma once
2 
3 #include <concepts>
4 #include <stdexcept>
5 #include "trace.h"
6 
7 namespace coro {
8 
10 
16 class std_allocator {};
17 
19 
23 
24 template<typename T>
25 concept coro_allocator_global = requires(std::size_t sz, void *ptr) {
26  {T::alloc(sz)} -> std::same_as<void *>;
27  {T::dealloc(ptr, sz)}->std::same_as<void>;
28 };
29 
30 template<typename T>
31 concept coro_allocator_local = (!coro_allocator_global<T> && requires(T a, std::size_t sz, void *ptr) {
32  {a.alloc(sz)} -> std::same_as<void *>;
33  {T::dealloc(ptr, sz)}->std::same_as<void>;
34 });
35 
36 
37 template<typename T>
38 concept coro_allocator = std::same_as<T, std_allocator> || std::same_as<T, const std_allocator>
39  || coro_allocator_local<T> || coro_allocator_global<T>;
40 
41 
43 
50 public:
51 
52  reusable_allocator() = default;
54  reusable_allocator &operator=(reusable_allocator &) = delete;
55 
56  void *alloc(std::size_t size) {
57  if (sz < size) {
58  operator delete(ptr);
59  ptr = operator new(sz = size);
60  }
61  return ptr;
62  }
63 
64  static void dealloc(void *, std::size_t) {}
65 
66 protected:
67  void *ptr = nullptr;
68  std::size_t sz = 0;
69 };
70 
71 static_assert(coro_allocator<reusable_allocator>);
72 static_assert(coro_allocator<std_allocator>);
73 
74 
76 template<coro_allocator Alloc>
78 
79 template<>
81 public:
82 #ifdef LIBCORO_ENABLE_TRACE
83  void *operator new(std::size_t sz) {
84  void *ptr = ::operator new(sz);
85  trace::on_create(ptr, sz);
86  return ptr;
87  }
88  void operator delete(void *ptr, std::size_t sz) {
89  trace::on_destroy(ptr, sz);
90  ::operator delete(ptr);
91  }
92 #endif
93 };
94 
95 template<>
96 class coro_allocator_helper<const std_allocator>: public coro_allocator_helper<std_allocator> {
97 public:
98 
99 };
100 
101 
102 template<coro_allocator_local Alloc>
103 class coro_allocator_helper<Alloc> {
104 public:
105 
106 
107  void operator delete(void *ptr, std::size_t sz) {
108  trace::on_destroy(ptr, sz);
109  Alloc::dealloc(ptr, sz);
110  }
111 
112  template<typename ... Args>
113  void *operator new(std::size_t sz, Alloc &a, Args && ...) {
114  void *ptr = a.alloc(sz);
115  trace::on_create(ptr, sz);
116  return ptr;
117  }
118 
119  template<typename ... Args>
120  void operator delete(void *, Alloc &, Args && ...) {
121  throw std::logic_error("unreachable");
122  }
123 
124 
125  template<typename This, typename ... Args>
126  void *operator new(std::size_t sz, This &, Alloc &a, Args && ...) {
127  void *ptr = a.alloc(sz);
128  trace::on_create(ptr, sz);
129  return ptr;
130  }
131 
132  template<typename This, typename ... Args>
133  void operator delete(void *, This &, Alloc &, Args && ...) {
134  throw std::logic_error("unreachable");
135  }
136 
137 #ifndef __clang__
138  //clang 15+ doesn't like operator new declared as private
139  //so we left it undefined. This ensures that standard allocator will not be used
140 private:
141 #endif
142  void *operator new(std::size_t sz);
143 
144 
145 
146 };
147 
148 template<coro_allocator_global Alloc>
149 class coro_allocator_helper<Alloc> {
150 public:
151  void operator delete(void *ptr, std::size_t sz) {
152  trace::on_destroy(ptr, sz);
153  Alloc::dealloc(ptr, sz);
154  }
155 
156  void *operator new(std::size_t sz) {
157  void *ptr = Alloc::alloc(sz);;
158  trace::on_create(ptr, sz);
159  return ptr;
160  }
161 };
162 
163 template<typename T>
164 concept memory_resource_pointer = requires(T x, std::size_t sz, void *ptr) {
165  {x->allocate(sz)} ->std::same_as<void *>;
166  {x->deallocate(ptr,sz)} ->std::same_as<void>;
167  requires std::copy_constructible<T>;
168 };
169 
170 
172 
188 template<memory_resource_pointer Res>
190 public:
191  template<std::convertible_to<Res> T>
192  pmr_allocator(T &&resource):_memory_resource(std::forward<Res>(resource)) {}
193 
194  void *alloc(std::size_t sz) {
195  auto needsz = sz + sizeof(Res);
196  void *ptr = _memory_resource->allocate(needsz);
197  Res *resptr = reinterpret_cast<Res *>(reinterpret_cast<char *>(ptr)+sz);
198  std::construct_at(resptr, _memory_resource);
199  return ptr;
200  }
201 
202  static void dealloc(void *ptr, std::size_t sz) {
203  auto needsz = sz + sizeof(Res);
204  Res *resptr = reinterpret_cast<Res *>(reinterpret_cast<char *>(ptr)+sz);
205  Res memory_res ( std::move(*resptr));
206  std::destroy_at(resptr);
207  memory_res->deallocate(ptr, needsz);
208  }
209 
210 protected:
211  Res _memory_resource;
212 };
213 
214 }
215 
216 
217 
218 
inherit this class to include coro_allocator into your promise_type
Definition: allocator.h:77
Creates libcoro compatible allocator which uses an instance of std::pmr::memory_resource for allocati...
Definition: allocator.h:189
Handles allocation of single coroutine, if it is repeatedly allocated and deallocated.
Definition: allocator.h:49
represents standard allocator for coroutines
Definition: allocator.h:16
constexpr std_allocator standard_allocator
Global instance for std_allocator which can be used anywhere the allocator is requested.
Definition: allocator.h:22
void on_create(const void *, std::size_t)
Record creation of an coroutine.
Definition: trace.h:362
void on_destroy(const void *, std::size_t)
Record destruction of an coroutine.
Definition: trace.h:371
main namespace
Definition: aggregator.h:8