Skip to content

Commit 8b59295

Browse files
committed
[Runtime] Change protocol conformance scanning to use a concurrent array rather than a locked vector.
rdar://problem/37173156
1 parent ae14c16 commit 8b59295

File tree

2 files changed

+205
-160
lines changed

2 files changed

+205
-160
lines changed

include/swift/Runtime/Concurrent.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <functional>
1717
#include <stdint.h>
1818
#include "llvm/Support/Allocator.h"
19+
#include "Mutex.h"
1920

2021
#if defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__HAIKU__)
2122
#include <stdio.h>
@@ -406,6 +407,91 @@ class ConcurrentMap
406407
}
407408
};
408409

410+
411+
template <class ElemTy> struct ConcurrentReadableArray {
412+
private:
413+
struct Storage {
414+
std::atomic<size_t> Count;
415+
typename std::aligned_storage<sizeof(ElemTy), alignof(ElemTy)>::type Elem;
416+
417+
ElemTy *data() {
418+
return reinterpret_cast<ElemTy *>(&Elem);
419+
}
420+
};
421+
422+
std::atomic<size_t> Capacity;
423+
std::atomic<size_t> ReaderCount;
424+
std::atomic<Storage *> Elements;
425+
Mutex WriterLock;
426+
std::vector<Storage *> FreeList;
427+
428+
Storage *allocate(size_t capacity) {
429+
auto size = sizeof(Storage) + (capacity - 1) * sizeof(Storage().Elem);
430+
auto *ptr = reinterpret_cast<Storage *>(malloc(size));
431+
ptr->Count.store(0, std::memory_order_relaxed);
432+
return ptr;
433+
}
434+
435+
void deallocate(Storage *storage) {
436+
if (storage == nullptr) return;
437+
438+
auto *data = storage->data();
439+
for (size_t i = 0; i < storage->Count; i++) {
440+
data[i].~ElemTy();
441+
}
442+
free(storage);
443+
}
444+
445+
446+
public:
447+
void push_back(const ElemTy &elem) {
448+
ScopedLock guard(WriterLock);
449+
450+
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);
459+
460+
storage = newStorage;
461+
Capacity = Capacity * 2;
462+
Elements.store(storage, std::memory_order_release);
463+
}
464+
465+
auto Count = storage->Count.load(std::memory_order_relaxed);
466+
storage->data()[Count] = elem;
467+
storage->Count.store(Count + 1, std::memory_order_release);
468+
469+
if (ReaderCount.load(std::memory_order_relaxed) == 0)
470+
for (Storage *storage : FreeList)
471+
deallocate(storage);
472+
}
473+
474+
template <class F> auto read(F f) -> decltype(f(nullptr, 0)) {
475+
ReaderCount.fetch_add(1, std::memory_order_relaxed);
476+
auto *storage = Elements.load(std::memory_order_consume);
477+
auto count = storage->Count.load(std::memory_order_acquire);
478+
auto *ptr = storage->data();
479+
480+
decltype(f(nullptr, 0)) result = f(ptr, count);
481+
482+
ReaderCount.fetch_sub(1, std::memory_order_relaxed);
483+
484+
return result;
485+
}
486+
487+
/// Get the current count. It's just a snapshot and may be obsolete immediately.
488+
size_t count() {
489+
return read([](ElemTy *ptr, size_t count) -> size_t {
490+
return count;
491+
});
492+
}
493+
};
494+
409495
} // end namespace swift
410496

411497
#endif // SWIFT_RUNTIME_CONCURRENTUTILS_H

0 commit comments

Comments
 (0)