QuantumLibrary
quantum_coroutine_pool_allocator_impl.h
1 /*
2 ** Copyright 2018 Bloomberg Finance L.P.
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 ** http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16 //NOTE: DO NOT INCLUDE DIRECTLY
17 #include <type_traits>
18 #include <algorithm>
19 #include <assert.h>
20 
21 #if defined(BOOST_USE_VALGRIND)
22  #include <valgrind/valgrind.h>
23 #endif
24 
25 //##############################################################################################
26 //#################################### IMPLEMENTATIONS #########################################
27 //##############################################################################################
28 namespace Bloomberg {
29 namespace quantum {
30 
31 template <typename STACK_TRAITS>
33  _size(size),
34  _blocks(new Header*[size]),
35  _freeBlocks(new index_type[size]),
36  _freeBlockIndex(size-1),
37  _numHeapAllocatedBlocks(0),
38  _stackSize(std::min(std::max(traits::default_size(), traits::minimum_size()), traits::maximum_size()))
39 {
40  if (!_blocks || !_freeBlocks) {
41  throw std::bad_alloc();
42  }
43  if (_size == 0) {
44  throw std::runtime_error("Invalid coroutine allocator pool size");
45  }
46  //pre-allocate all the coroutine stack blocks
47  for (index_type i = 0; i < size; ++i) {
48  _blocks[i] = reinterpret_cast<Header*>(new char[_stackSize]);
49  if (!_blocks[i]) {
50  throw std::bad_alloc();
51  }
52  _blocks[i]->_pos = i; //mark position
53  }
54  //initialize the free block list
55  for (index_type i = 0; i < size; ++i) {
56  _freeBlocks[i] = i;
57  }
58 }
59 
60 template <typename STACK_TRAITS>
62 {
63  *this = other;
64 }
65 
66 template <typename STACK_TRAITS>
68 {
69  _size = other._size;
70  _blocks = other._blocks;
71  _freeBlocks = other._freeBlocks;
72  _freeBlockIndex = other._freeBlockIndex;
73  _numHeapAllocatedBlocks = other._numHeapAllocatedBlocks;
74 
75  // Reset other
76  other._blocks = nullptr;
77  other._freeBlocks = nullptr;
78  other._freeBlockIndex = -1;
79  other._numHeapAllocatedBlocks = 0;
80 }
81 
82 template <typename STACK_TRAITS>
84 {
85  for (size_t i = 0; i < _size; ++i) {
86  delete[] (char*)_blocks[i];
87  }
88  delete[] _blocks;
89  delete[] _freeBlocks;
90 }
91 
92 template <typename STACK_TRAITS>
93 boost::context::stack_context CoroutinePoolAllocator<STACK_TRAITS>::allocate() {
94  boost::context::stack_context ctx;
95  Header* block = nullptr;
96  {
97  SpinLock::Guard lock(_spinlock);
98  if (!isEmpty())
99  {
100  block = _blocks[_freeBlocks[_freeBlockIndex--]];
101  }
102  }
103  if (!block) {
104  // Use heap allocation
105  block = (Header*)new char[_stackSize];
106  if (!block) {
107  throw std::bad_alloc();
108  }
109  block->_pos = -1; //mark position as non-managed
110  SpinLock::Guard lock(_spinlock);
111  ++_numHeapAllocatedBlocks;
112  }
113  char* block_start = reinterpret_cast<char*>(block) + sizeof(Header);
114  ctx.size = _stackSize - sizeof(Header);
115  ctx.sp = block_start + ctx.size;
116  #if defined(BOOST_USE_VALGRIND)
117  ctx.valgrind_stack_id = VALGRIND_STACK_REGISTER(ctx.sp, block_start);
118  #endif
119  return ctx;
120 }
121 
122 template <typename STACK_TRAITS>
123 void CoroutinePoolAllocator<STACK_TRAITS>::deallocate(const boost::context::stack_context& ctx) {
124  if (!ctx.sp) {
125  return;
126  }
127 #if defined(BOOST_USE_VALGRIND)
128  VALGRIND_STACK_DEREGISTER(ctx.valgrind_stack_id);
129 #endif
130  if (isManaged(ctx)) {
131  //find index of the block
132  SpinLock::Guard lock(_spinlock);
133  _freeBlocks[++_freeBlockIndex] = blockIndex(ctx);
134  }
135  else {
136  delete[] (char*)getHeader(ctx);
137  SpinLock::Guard lock(_spinlock);
138  --_numHeapAllocatedBlocks;
139  assert(_numHeapAllocatedBlocks >= 0);
140  }
141 }
142 
143 template <typename STACK_TRAITS>
145 {
146  return _size - _freeBlockIndex - 1;
147 }
148 
149 template <typename STACK_TRAITS>
151 {
152  return _numHeapAllocatedBlocks;
153 }
154 
155 template <typename STACK_TRAITS>
157 {
158  return _freeBlockIndex == _size-1;
159 }
160 
161 template <typename STACK_TRAITS>
163 {
164  return _freeBlockIndex == -1;
165 }
166 
167 template <typename STACK_TRAITS>
169 CoroutinePoolAllocator<STACK_TRAITS>::getHeader(const boost::context::stack_context& ctx) const
170 {
171  return reinterpret_cast<Header*>(reinterpret_cast<char*>(ctx.sp) - ctx.size - sizeof(Header));
172 }
173 
174 template <typename STACK_TRAITS>
175 bool CoroutinePoolAllocator<STACK_TRAITS>::isManaged(const boost::context::stack_context& ctx) const
176 {
177  return blockIndex(ctx) != -1;
178 }
179 
180 template <typename STACK_TRAITS>
181 int CoroutinePoolAllocator<STACK_TRAITS>::blockIndex(const boost::context::stack_context& ctx) const
182 {
183  return getHeader(ctx)->_pos;
184 }
185 
186 }}
Definition: quantum_spinlock.h:71
STACK_TRAITS traits
Definition: quantum_coroutine_pool_allocator.h:46
Definition: quantum_buffer_impl.h:22
virtual ~CoroutinePoolAllocator()
Definition: quantum_coroutine_pool_allocator_impl.h:83
void deallocate(const boost::context::stack_context &ctx)
Definition: quantum_coroutine_pool_allocator_impl.h:123
Definition: quantum_stl_impl.h:23
Provides fast (quasi zero-time) in-place allocation for coroutines. Coroutine stacks are pre-allocate...
Definition: quantum_coroutine_pool_allocator.h:40
bool isEmpty() const
Definition: quantum_coroutine_pool_allocator_impl.h:162
CoroutinePoolAllocator(index_type size)
Definition: quantum_coroutine_pool_allocator_impl.h:32
bool isFull() const
Definition: quantum_coroutine_pool_allocator_impl.h:156
size_t allocatedBlocks() const
Definition: quantum_coroutine_pool_allocator_impl.h:144
size_t allocatedHeapBlocks() const
Definition: quantum_coroutine_pool_allocator_impl.h:150
CoroutinePoolAllocator & operator=(const this_type &)=delete
uint16_t index_type
Definition: quantum_coroutine_pool_allocator.h:45
boost::context::stack_context allocate()
Definition: quantum_coroutine_pool_allocator_impl.h:93