68 ::detail::log2i(minBuffSize) +
69 ((Index{1} << ::detail::log2i(minBuffSize)) == minBuffSize ? 0 : 1)),
70 kBufferSize(Index{1} << kLog2BuffSize),
71 kMask((Index{1} << kLog2BuffSize) - 1),
78 allocatedSize_.store(kBufferSize, std::memory_order_relaxed);
79 if (initialSize > 0) {
88 : kLog2BuffSize(other.kLog2BuffSize),
89 kBufferSize(other.kBufferSize),
91 pos_(other.pos_.load(std::memory_order_relaxed)),
92 allocatedSize_(other.allocatedSize_.load(std::memory_order_relaxed)),
93 buffersSize_(other.buffersSize_),
94 buffersPos_(other.buffersPos_) {
96 std::is_trivially_copyable<T>::value,
97 "ConcurrentObjectArena copy constructor uses memcpy; T must be trivially copyable.");
98 T** otherBuffers = other.buffers_.load(std::memory_order_acquire);
99 T** newBuffers =
new T*[buffersSize_];
100 for (Index i = 0; i < buffersSize_; ++i) {
101 void* ptr = detail::alignedMalloc(kBufferSize *
sizeof(T), alignment);
102#if defined(__cpp_exceptions)
104 throw std::bad_alloc();
106 std::memcpy(ptr, otherBuffers[i], kBufferSize *
sizeof(T));
107 newBuffers[i] =
static_cast<T*
>(ptr);
109 buffers_.store(newBuffers, std::memory_order_release);
128 T** buffers = buffers_.load(std::memory_order_acquire);
130 for (Index i = 0; i < buffersPos_; i++)
131 detail::alignedFree(buffers[i]);
135 for (T** p : deleteLater_)
142 ConcurrentObjectArena<T, Index, alignment>& operator=(
143 ConcurrentObjectArena<T, Index, alignment>
const& other) {
144 ConcurrentObjectArena<T, Index, alignment> copy(other);
152 ConcurrentObjectArena<T, Index, alignment>& operator=(
153 ConcurrentObjectArena<T, Index, alignment>&& other)
noexcept {
168 Index oldPos = pos_.load(std::memory_order_relaxed);
171 Index curSize = allocatedSize_.load(std::memory_order_acquire);
173 if (oldPos + delta >= curSize) {
174 const std::lock_guard<std::mutex> guard(resizeMutex_);
175 curSize = allocatedSize_.load(std::memory_order_relaxed);
176 while (oldPos + delta >= curSize) {
178 allocatedSize_.store(curSize + kBufferSize, std::memory_order_release);
179 curSize = curSize + kBufferSize;
183 newPos = oldPos + delta;
184 }
while (!std::atomic_compare_exchange_weak_explicit(
185 &pos_, &oldPos, newPos, std::memory_order_release, std::memory_order_relaxed));
187 constructObjects(oldPos, oldPos + delta);
200 inline const T& operator[](
const Index index)
const {
201 const Index bufIndex = index >> kLog2BuffSize;
202 const Index i = index & kMask;
204 return buffers_.load(std::memory_order_acquire)[bufIndex][i];
215 inline T& operator[](
const Index index) {
216 return const_cast<T&
>(
217 const_cast<const ConcurrentObjectArena<T, Index, alignment>&
>(*this)[index]);
226 return pos_.load(std::memory_order_relaxed);
234 return allocatedSize_.load(std::memory_order_relaxed);
251 return buffers_.load(std::memory_order_acquire)[index];
260 return buffers_.load(std::memory_order_acquire)[index];
270 assert(index < numBuffs);
272 if (index < numBuffs - 1)
275 return pos_.load(std::memory_order_relaxed) - (kBufferSize * (numBuffs - 1));
288 swap(lhs.kLog2BuffSize, rhs.kLog2BuffSize);
289 swap(lhs.kBufferSize, rhs.kBufferSize);
290 swap(lhs.kMask, rhs.kMask);
292 const Index rhs_pos = rhs.pos_.load(std::memory_order_relaxed);
293 rhs.pos_.store(lhs.pos_.load(std::memory_order_relaxed), std::memory_order_relaxed);
294 lhs.pos_.store(rhs_pos, std::memory_order_relaxed);
296 const Index rhs_allocatedSize = rhs.allocatedSize_.load(std::memory_order_relaxed);
297 rhs.allocatedSize_.store(
298 lhs.allocatedSize_.load(std::memory_order_relaxed), std::memory_order_relaxed);
299 lhs.allocatedSize_.store(rhs_allocatedSize, std::memory_order_relaxed);
301 T**
const rhs_buffers = rhs.buffers_.load(std::memory_order_acquire);
302 rhs.buffers_.store(lhs.buffers_.load(std::memory_order_acquire), std::memory_order_release);
303 lhs.buffers_.store(rhs_buffers, std::memory_order_release);
305 swap(lhs.buffersSize_, rhs.buffersSize_);
306 swap(lhs.buffersPos_, rhs.buffersPos_);
307 swap(lhs.deleteLater_, rhs.deleteLater_);
311 void allocateBuffer() {
312 void* ptr = detail::alignedMalloc(kBufferSize *
sizeof(T), alignment);
313#if defined(__cpp_exceptions)
315 throw std::bad_alloc();
318 if (buffersPos_ < buffersSize_) {
319 buffers_.load(std::memory_order_acquire)[buffersPos_++] =
static_cast<T*
>(ptr);
321 const Index oldBuffersSize = buffersSize_;
322 T** oldBuffers = buffers_.load(std::memory_order_acquire);
324 buffersSize_ = oldBuffersSize == 0 ? 2 : oldBuffersSize * 2;
325 T** newBuffers =
new T*[buffersSize_];
327 if (oldBuffers !=
nullptr) {
328 std::memcpy(newBuffers, oldBuffers,
sizeof(T*) * oldBuffersSize);
329 deleteLater_.push_back(oldBuffers);
332 newBuffers[buffersPos_++] =
static_cast<T*
>(ptr);
333 buffers_.store(newBuffers, std::memory_order_release);
337 void constructObjects(
const Index beginIndex,
const Index endIndex) {
338 const Index startBuffer = beginIndex >> kLog2BuffSize;
339 const Index endBuffer = endIndex >> kLog2BuffSize;
341 Index bufStart = beginIndex & kMask;
342 for (Index b = startBuffer; b <= endBuffer; ++b) {
343 T* buf = buffers_.load(std::memory_order_acquire)[b];
344 const Index bufEnd = b == endBuffer ? (endIndex & kMask) : kBufferSize;
345 for (Index i = bufStart; i < bufEnd; ++i)
357 std::mutex resizeMutex_;
363 std::atomic<Index> pos_;
364 std::atomic<Index> allocatedSize_;
366 std::atomic<T**> buffers_;
369 std::vector<T**> deleteLater_;