Skip to content

Commit a296eda

Browse files
committed
[Runtime] Clean up and lightly document ConcurrentReadableArray. Check for malloc failure. Use placement new when appending values.
rdar://problem/37173156
1 parent d53bb4d commit a296eda

File tree

1 file changed

+25
-12
lines changed

1 file changed

+25
-12
lines changed

include/swift/Runtime/Concurrent.h

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
#ifndef SWIFT_RUNTIME_CONCURRENTUTILS_H
1313
#define SWIFT_RUNTIME_CONCURRENTUTILS_H
1414
#include <iterator>
15+
#include <algorithm>
1516
#include <atomic>
1617
#include <functional>
1718
#include <stdint.h>
1819
#include "llvm/Support/Allocator.h"
20+
#include "Debug.h"
1921
#include "Mutex.h"
2022

2123
#if defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__HAIKU__)
@@ -408,8 +410,15 @@ class ConcurrentMap
408410
};
409411

410412

413+
/// An append-only array that can be read without taking locks. Writes
414+
/// are still locked and serialized, but only with respect to other
415+
/// writes.
411416
template <class ElemTy> struct ConcurrentReadableArray {
412417
private:
418+
/// The struct used for the array's storage. The `Elem` member is
419+
/// considered to be the first element of a variable-length array,
420+
/// whose size is determined by the allocation. The `Capacity` member
421+
/// from `ConcurrentReadableArray` indicates how large it can be.
413422
struct Storage {
414423
std::atomic<size_t> Count;
415424
typename std::aligned_storage<sizeof(ElemTy), alignof(ElemTy)>::type Elem;
@@ -419,7 +428,7 @@ template <class ElemTy> struct ConcurrentReadableArray {
419428
}
420429
};
421430

422-
std::atomic<size_t> Capacity;
431+
size_t Capacity;
423432
std::atomic<size_t> ReaderCount;
424433
std::atomic<Storage *> Elements;
425434
Mutex WriterLock;
@@ -428,6 +437,7 @@ template <class ElemTy> struct ConcurrentReadableArray {
428437
Storage *allocate(size_t capacity) {
429438
auto size = sizeof(Storage) + (capacity - 1) * sizeof(Storage().Elem);
430439
auto *ptr = reinterpret_cast<Storage *>(malloc(size));
440+
if (!ptr) swift::crash("Could not allocate memory.");
431441
ptr->Count.store(0, std::memory_order_relaxed);
432442
return ptr;
433443
}
@@ -442,35 +452,38 @@ template <class ElemTy> struct ConcurrentReadableArray {
442452
free(storage);
443453
}
444454

445-
446455
public:
447456
void push_back(const ElemTy &elem) {
448457
ScopedLock guard(WriterLock);
449458

450459
auto *storage = Elements.load(std::memory_order_relaxed);
451-
if (storage == nullptr) {
452-
storage = allocate(16);
453-
Capacity = 16;
454-
Elements.store(storage, std::memory_order_release);
455-
} else if (storage->Count >= Capacity) {
456-
auto *newStorage = allocate(Capacity * 2);
457-
std::copy(storage->data(), storage->data() + storage->Count, newStorage->data());
458-
FreeList.push_back(storage);
460+
auto count = storage ? storage->Count.load(std::memory_order_relaxed) : 0;
461+
if (count >= Capacity) {
462+
auto newCapacity = std::max((size_t)16, count * 2);
463+
auto *newStorage = allocate(newCapacity);
464+
if (storage) {
465+
std::copy(storage->data(), storage->data() + count, newStorage->data());
466+
FreeList.push_back(storage);
467+
}
459468

460469
storage = newStorage;
461-
Capacity = Capacity * 2;
470+
Capacity = newCapacity;
462471
Elements.store(storage, std::memory_order_release);
463472
}
464473

465474
auto Count = storage->Count.load(std::memory_order_relaxed);
466-
storage->data()[Count] = elem;
475+
new(&storage->data()[Count]) ElemTy(elem);
467476
storage->Count.store(Count + 1, std::memory_order_release);
468477

469478
if (ReaderCount.load(std::memory_order_relaxed) == 0)
470479
for (Storage *storage : FreeList)
471480
deallocate(storage);
472481
}
473482

483+
/// Read the contents of the array. The parameter `f` is called with
484+
/// two parameters: a pointer to the elements in the array, and the
485+
/// count. This represents a snapshot of the contents at the time
486+
/// `read` was called. The pointer becomes invalid after `f` returns.
474487
template <class F> auto read(F f) -> decltype(f(nullptr, 0)) {
475488
ReaderCount.fetch_add(1, std::memory_order_relaxed);
476489
auto *storage = Elements.load(std::memory_order_consume);

0 commit comments

Comments
 (0)