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