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
18 //##############################################################################################
19 //#################################### IMPLEMENTATIONS #########################################
20 //##############################################################################################
21
22 #include <type_traits>
23 #include <cassert>
24 #include <stdexcept>
25 #include <algorithm>
26
27 namespace Bloomberg {
28 namespace quantum {
29
30 template <typename T>
ContiguousPoolManager()31 ContiguousPoolManager<T>::ContiguousPoolManager() :
32 _control(std::make_shared<Control>())
33 {
34 }
35
36 template <typename T>
ContiguousPoolManager(aligned_type * buffer,index_type size)37 ContiguousPoolManager<T>::ContiguousPoolManager(aligned_type* buffer, index_type size) :
38 _control(std::make_shared<Control>())
39 {
40 setBuffer(buffer, size);
41 }
42
43 template <typename T>
44 template <typename U>
ContiguousPoolManager(const ContiguousPoolManager<U> & other)45 ContiguousPoolManager<T>::ContiguousPoolManager(const ContiguousPoolManager<U>& other) :
46 _control(std::reinterpret_pointer_cast<Control>(other._control))
47 {
48 if (!_control || !_control->_buffer)
49 {
50 throw std::invalid_argument("Invalid allocator");
51 }
52 //normalize size of buffer
53 index_type newSize = std::min(_control->_size, (index_type)resize<U,T>(_control->_size));
54 _control->_size = newSize; //resize buffer
55 _control->_freeBlockIndex = newSize-1;
56 }
57
58 template <typename T>
59 template <typename U>
ContiguousPoolManager(ContiguousPoolManager<U> && other)60 ContiguousPoolManager<T>::ContiguousPoolManager(ContiguousPoolManager<U>&& other) :
61 _control(std::move(other._control))
62 {
63 if (!_control || !_control->_buffer)
64 {
65 throw std::invalid_argument("Invalid allocator");
66 }
67 //normalize size of buffer
68 index_type newSize = std::min(_control->_size, (index_type)resize<U,T>(_control->_size));
69 _control->_size = newSize; //resize buffer
70 _control->_freeBlockIndex = newSize-1;
71 }
72
73 template <typename T>
setBuffer(aligned_type * buffer,index_type size)74 void ContiguousPoolManager<T>::setBuffer(aligned_type *buffer, index_type size)
75 {
76 if (!_control) {
77 throw std::bad_alloc();
78 }
79 if (!buffer) {
80 throw std::invalid_argument("Null buffer");
81 }
82 if (size == 0) {
83 throw std::invalid_argument("Invalid allocator pool size of zero");
84 }
85 _control->_size = size;
86 _control->_buffer = buffer;
87 if (_control->_freeBlocks) {
88 delete[] _control->_freeBlocks;
89 }
90 _control->_freeBlocks = new index_type[size];
91 if (!_control->_freeBlocks) {
92 throw std::bad_alloc();
93 }
94 //build the free stack
95 for (index_type i = 0; i < size; ++i) {
96 _control->_freeBlocks[i] = i;
97 }
98 _control->_freeBlockIndex = size-1;
99 }
100
101 template <typename T>
address(reference x)102 typename ContiguousPoolManager<T>::pointer ContiguousPoolManager<T>::address(reference x) const
103 {
104 return &x;
105 }
106
107 template <typename T>
address(const_reference x)108 typename ContiguousPoolManager<T>::const_pointer ContiguousPoolManager<T>::address(const_reference x) const
109 {
110 return &x;
111 }
112
113 template <typename T>
max_size()114 typename ContiguousPoolManager<T>::size_type ContiguousPoolManager<T>::max_size() const
115 {
116 return 1; //only 1 supported for now
117 }
118
119 template <typename T>
120 template <typename... Args >
construct(T * p,Args &&...args)121 void ContiguousPoolManager<T>::construct(T* p, Args&&... args)
122 {
123 new((void *)p) T(std::forward<Args>(args)...); // construct in-place
124 }
125
126 template <typename T>
destroy(pointer p)127 void ContiguousPoolManager<T>::destroy(pointer p)
128 {
129 if (p != nullptr)
130 {
131 p->~T();
132 }
133 }
134
135 template <typename T>
136 typename ContiguousPoolManager<T>::pointer
allocate(size_type n,const_pointer)137 ContiguousPoolManager<T>::allocate(size_type n, const_pointer)
138 {
139 assert(bufferStart());
140 {
141 SpinLock::Guard lock(_control->_spinlock);
142 if (findContiguous(static_cast<index_type>(n)))
143 {
144 _control->_freeBlockIndex -= (n - 1);
145 return reinterpret_cast<pointer>(&_control->_buffer[_control->_freeBlocks[_control->_freeBlockIndex--]]);
146 }
147 // Use heap allocation
148 ++_control->_numHeapAllocatedBlocks;
149 }
150 return (pointer)new char[sizeof(value_type)];
151 }
152
153 template <typename T>
deallocate(pointer p,size_type n)154 void ContiguousPoolManager<T>::deallocate(pointer p, size_type n)
155 {
156 if (p == nullptr) {
157 return;
158 }
159 assert(bufferStart());
160 if (isManaged(p)) {
161 //find index of the block and return the individual blocks to the free pool
162 SpinLock::Guard lock(_control->_spinlock);
163 for (size_type i = 0; i < n; ++i) {
164 _control->_freeBlocks[++_control->_freeBlockIndex] = blockIndex(p+i);
165 }
166 }
167 else {
168 delete[] (char*)p;
169 SpinLock::Guard lock(_control->_spinlock);
170 --_control->_numHeapAllocatedBlocks;
171 assert(_control->_numHeapAllocatedBlocks >= 0);
172 }
173 }
174
175 template <typename T>
176 template <typename... Args >
create(Args &&...args)177 typename ContiguousPoolManager<T>::pointer ContiguousPoolManager<T>::create(Args&&... args)
178 {
179 T* p = allocate();
180 construct(p, std::forward<Args>(args)...);
181 return p;
182 }
183
184 template <typename T>
dispose(pointer p)185 void ContiguousPoolManager<T>::dispose(pointer p)
186 {
187 destroy(p);
188 deallocate(p);
189 }
190
191 template <typename T>
allocatedBlocks()192 size_t ContiguousPoolManager<T>::allocatedBlocks() const
193 {
194 return _control->_size ? _control->_size - _control->_freeBlockIndex - 1 : 0;
195 }
196
197 template <typename T>
allocatedHeapBlocks()198 size_t ContiguousPoolManager<T>::allocatedHeapBlocks() const
199 {
200 return _control->_numHeapAllocatedBlocks;
201 }
202
203 template <typename T>
isFull()204 bool ContiguousPoolManager<T>::isFull() const
205 {
206 return _control->_freeBlockIndex == _control->_size-1;
207 }
208
209 template <typename T>
isEmpty()210 bool ContiguousPoolManager<T>::isEmpty() const
211 {
212 return _control->_freeBlockIndex == -1;
213 }
214
215 template <typename T>
size()216 typename ContiguousPoolManager<T>::index_type ContiguousPoolManager<T>::size() const
217 {
218 return _control->_size;
219 }
220
221 template <typename T>
222 ContiguousPoolManager<T>::operator bool() const
223 {
224 return _control != nullptr;
225 }
226
227 template <typename T>
bufferStart()228 typename ContiguousPoolManager<T>::pointer ContiguousPoolManager<T>::bufferStart()
229 {
230 return reinterpret_cast<pointer>(_control->_buffer);
231 }
232
233 template <typename T>
bufferEnd()234 typename ContiguousPoolManager<T>::pointer ContiguousPoolManager<T>::bufferEnd()
235 {
236 return reinterpret_cast<pointer>(_control->_buffer + _control->_size);
237 }
238
239 template <typename T>
isManaged(pointer p)240 bool ContiguousPoolManager<T>::isManaged(pointer p)
241 {
242 return (bufferStart() <= p) && (p < bufferEnd());
243 }
244
245 template <typename T>
blockIndex(pointer p)246 typename ContiguousPoolManager<T>::index_type ContiguousPoolManager<T>::blockIndex(pointer p)
247 {
248 return static_cast<index_type>(reinterpret_cast<aligned_type*>(p) - _control->_buffer);
249 }
250
251 template <typename T>
findContiguous(index_type n)252 bool ContiguousPoolManager<T>::findContiguous(index_type n)
253 {
254 if ((_control->_freeBlockIndex + 1) < n) {
255 return false;
256 }
257 bool found = true;
258 aligned_type* last = &_control->_buffer[_control->_freeBlocks[_control->_freeBlockIndex]];
259 for (ssize_t i = _control->_freeBlockIndex-1; i > _control->_freeBlockIndex-n; --i) {
260 aligned_type* first = &_control->_buffer[_control->_freeBlocks[i]];
261 if ((last-first) != (_control->_freeBlockIndex-i)) {
262 return false;
263 }
264 }
265 return found;
266 }
267
268 }}
269
270