80 ::detail::log2i(minBuffSize) +
81 ((Index{1} << ::detail::log2i(minBuffSize)) == minBuffSize ? 0 : 1)),
82 kBufferSize(Index{1} << kLog2BuffSize),
83 kMask((Index{1} << kLog2BuffSize) - 1),
90 allocatedSize_.store(kBufferSize, std::memory_order_relaxed);
91 if (initialSize > 0) {
100 : kLog2BuffSize(other.kLog2BuffSize),
101 kBufferSize(other.kBufferSize),
103 pos_(other.pos_.load(std::memory_order_relaxed)),
104 allocatedSize_(other.allocatedSize_.load(std::memory_order_relaxed)),
105 buffersSize_(other.buffersSize_),
106 buffersPos_(other.buffersPos_),
109 std::is_trivially_copyable<T>::value,
110 "ConcurrentObjectArena copy constructor uses memcpy; T must be trivially copyable.");
111 T** otherBuffers = other.buffers_.load(std::memory_order_acquire);
112 T** newBuffers =
new T*[buffersSize_];
115 for (Index i = 0; i < buffersPos_; ++i) {
116 void* ptr = detail::alignedMallocOrThrow(kBufferSize *
sizeof(T), alignment);
117 std::memcpy(ptr, otherBuffers[i], kBufferSize *
sizeof(T));
118 newBuffers[i] =
static_cast<T*
>(ptr);
120 buffers_.store(newBuffers, std::memory_order_release);
139 T** buffers = buffers_.load(std::memory_order_acquire);
141 destroyObjects(buffers);
143 for (Index i = 0; i < buffersPos_; i++)
144 detail::alignedFree(buffers[i]);
148 for (T** p : deleteLater_)
155 ConcurrentObjectArena<T, Index, alignment>& operator=(
156 ConcurrentObjectArena<T, Index, alignment>
const& other) {
157 ConcurrentObjectArena<T, Index, alignment> copy(other);
165 ConcurrentObjectArena<T, Index, alignment>& operator=(
166 ConcurrentObjectArena<T, Index, alignment>&& other)
noexcept {
184 Index oldPos = pos_.load(std::memory_order_relaxed);
187 Index curSize = allocatedSize_.load(std::memory_order_acquire);
189 if (oldPos + delta >= curSize) {
190 const std::lock_guard<std::mutex> guard(resizeMutex_);
191 curSize = allocatedSize_.load(std::memory_order_relaxed);
192 while (oldPos + delta >= curSize) {
194 allocatedSize_.store(curSize + kBufferSize, std::memory_order_release);
195 curSize = curSize + kBufferSize;
199 newPos = oldPos + delta;
200 }
while (!std::atomic_compare_exchange_weak_explicit(
201 &pos_, &oldPos, newPos, std::memory_order_release, std::memory_order_relaxed));
203 Index built = oldPos;
204#if defined(__cpp_exceptions)
206 constructObjects(oldPos, newPos, built);
208 destroyRange(buffers_.load(std::memory_order_acquire), oldPos, built);
209 recordGap(oldPos, newPos);
213 constructObjects(oldPos, newPos, built);
227 inline const T& operator[](
const Index index)
const {
228 const Index bufIndex = index >> kLog2BuffSize;
229 const Index i = index & kMask;
231 return buffers_.load(std::memory_order_acquire)[bufIndex][i];
242 inline T& operator[](
const Index index) {
243 return const_cast<T&
>(
244 const_cast<const ConcurrentObjectArena<T, Index, alignment>&
>(*this)[index]);
253 return pos_.load(std::memory_order_relaxed);
261 return allocatedSize_.load(std::memory_order_relaxed);
278 return buffers_.load(std::memory_order_acquire)[index];
287 return buffers_.load(std::memory_order_acquire)[index];
297 assert(index < numBuffs);
299 if (index < numBuffs - 1)
302 return pos_.load(std::memory_order_relaxed) - (kBufferSize * (numBuffs - 1));
315 swap(lhs.kLog2BuffSize, rhs.kLog2BuffSize);
316 swap(lhs.kBufferSize, rhs.kBufferSize);
317 swap(lhs.kMask, rhs.kMask);
319 const Index rhs_pos = rhs.pos_.load(std::memory_order_relaxed);
320 rhs.pos_.store(lhs.pos_.load(std::memory_order_relaxed), std::memory_order_relaxed);
321 lhs.pos_.store(rhs_pos, std::memory_order_relaxed);
323 const Index rhs_allocatedSize = rhs.allocatedSize_.load(std::memory_order_relaxed);
324 rhs.allocatedSize_.store(
325 lhs.allocatedSize_.load(std::memory_order_relaxed), std::memory_order_relaxed);
326 lhs.allocatedSize_.store(rhs_allocatedSize, std::memory_order_relaxed);
328 T**
const rhs_buffers = rhs.buffers_.load(std::memory_order_acquire);
329 rhs.buffers_.store(lhs.buffers_.load(std::memory_order_acquire), std::memory_order_release);
330 lhs.buffers_.store(rhs_buffers, std::memory_order_release);
332 swap(lhs.buffersSize_, rhs.buffersSize_);
333 swap(lhs.buffersPos_, rhs.buffersPos_);
334 swap(lhs.deleteLater_, rhs.deleteLater_);
335 swap(lhs.gaps_, rhs.gaps_);
339 void allocateBuffer() {
340 void* ptr = detail::alignedMallocOrThrow(kBufferSize *
sizeof(T), alignment);
342 if (buffersPos_ < buffersSize_) {
343 buffers_.load(std::memory_order_acquire)[buffersPos_++] =
static_cast<T*
>(ptr);
345 const Index oldBuffersSize = buffersSize_;
346 T** oldBuffers = buffers_.load(std::memory_order_acquire);
348 buffersSize_ = oldBuffersSize == 0 ? 2 : oldBuffersSize * 2;
349 T** newBuffers =
new T*[buffersSize_];
351 if (oldBuffers !=
nullptr) {
352 std::memcpy(newBuffers, oldBuffers,
sizeof(T*) * oldBuffersSize);
353 deleteLater_.push_back(oldBuffers);
356 newBuffers[buffersPos_++] =
static_cast<T*
>(ptr);
357 buffers_.store(newBuffers, std::memory_order_release);
363 void constructObjects(
const Index beginIndex,
const Index endIndex, Index& built) {
364 const Index startBuffer = beginIndex >> kLog2BuffSize;
365 const Index endBuffer = endIndex >> kLog2BuffSize;
367 Index bufStart = beginIndex & kMask;
368 for (Index b = startBuffer; b <= endBuffer; ++b) {
369 T* buf = buffers_.load(std::memory_order_acquire)[b];
370 const Index bufEnd = b == endBuffer ? (endIndex & kMask) : kBufferSize;
371 for (Index i = bufStart; i < bufEnd; ++i) {
373 built = (b << kLog2BuffSize) + i + 1;
380 void destroyRange(T** buffers, Index beginIndex,
const Index endIndex) {
381 for (; beginIndex < endIndex; ++beginIndex) {
382 buffers[beginIndex >> kLog2BuffSize][beginIndex & kMask].~T();
387 void destroyObjects(T** buffers) {
388 if (std::is_trivially_destructible<T>::value || buffers ==
nullptr) {
391 std::sort(gaps_.begin(), gaps_.end());
392 const Index end = pos_.load(std::memory_order_relaxed);
394 for (
const std::pair<Index, Index>& gap : gaps_) {
395 destroyRange(buffers, next, gap.first);
398 destroyRange(buffers, next, end);
403 void recordGap(
const Index beginIndex,
const Index endIndex) {
404 const std::lock_guard<std::mutex> guard(resizeMutex_);
405 gaps_.emplace_back(beginIndex, endIndex);
413 std::mutex resizeMutex_;
419 std::atomic<Index> pos_;
420 std::atomic<Index> allocatedSize_;
422 std::atomic<T**> buffers_;
425 std::vector<T**> deleteLater_;
427 std::vector<std::pair<Index, Index>> gaps_;