[K/N][Runtime] Add a lock method to thread registry
This commit is contained in:
@@ -53,7 +53,7 @@ template <typename Traits>
|
|||||||
typename Traits::ObjectFactory::FinalizerQueue Sweep(typename Traits::ObjectFactory& objectFactory) noexcept {
|
typename Traits::ObjectFactory::FinalizerQueue Sweep(typename Traits::ObjectFactory& objectFactory) noexcept {
|
||||||
typename Traits::ObjectFactory::FinalizerQueue finalizerQueue;
|
typename Traits::ObjectFactory::FinalizerQueue finalizerQueue;
|
||||||
|
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (Traits::TryResetMark(*it)) {
|
if (Traits::TryResetMark(*it)) {
|
||||||
++it;
|
++it;
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ public:
|
|||||||
testing::Mock::VerifyAndClear(&finalizerHook());
|
testing::Mock::VerifyAndClear(&finalizerHook());
|
||||||
// TODO: Figure out a better way to clear up the stuff.
|
// TODO: Figure out a better way to clear up the stuff.
|
||||||
EXPECT_CALL(finalizerHook(), Call(testing::_)).Times(testing::AnyNumber());
|
EXPECT_CALL(finalizerHook(), Call(testing::_)).Times(testing::AnyNumber());
|
||||||
for (auto node : objectFactory_.Iter()) {
|
for (auto node : objectFactory_.LockForIter()) {
|
||||||
auto* obj = node->IsArray() ? node->GetArrayHeader()->obj() : node->GetObjHeader();
|
auto* obj = node->IsArray() ? node->GetArrayHeader()->obj() : node->GetObjHeader();
|
||||||
if (auto* extraObject = mm::ExtraObjectData::Get(obj)) {
|
if (auto* extraObject = mm::ExtraObjectData::Get(obj)) {
|
||||||
extraObject->ClearWeakReferenceCounter();
|
extraObject->ClearWeakReferenceCounter();
|
||||||
@@ -196,7 +196,7 @@ public:
|
|||||||
|
|
||||||
KStdVector<ObjHeader*> Alive() {
|
KStdVector<ObjHeader*> Alive() {
|
||||||
KStdVector<ObjHeader*> objects;
|
KStdVector<ObjHeader*> objects;
|
||||||
for (auto node : objectFactory_.Iter()) {
|
for (auto node : objectFactory_.LockForIter()) {
|
||||||
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
|
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
|
||||||
}
|
}
|
||||||
return objects;
|
return objects;
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ mm::ObjectFactory<gc::SameThreadMarkAndSweep>::FinalizerQueue gc::SameThreadMark
|
|||||||
}
|
}
|
||||||
|
|
||||||
KStdVector<ObjHeader*> graySet;
|
KStdVector<ObjHeader*> graySet;
|
||||||
for (auto& thread : mm::GlobalData::Instance().threadRegistry().Iter()) {
|
for (auto& thread : mm::GlobalData::Instance().threadRegistry().LockForIter()) {
|
||||||
// TODO: Maybe it's more efficient to do by the suspending thread?
|
// TODO: Maybe it's more efficient to do by the suspending thread?
|
||||||
thread.Publish();
|
thread.Publish();
|
||||||
for (auto* object : mm::ThreadRootSet(thread)) {
|
for (auto* object : mm::ThreadRootSet(thread)) {
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ KStdVector<ObjHeader*> Alive(mm::ThreadData& threadData) {
|
|||||||
for (auto node : threadData.objectFactoryThreadQueue()) {
|
for (auto node : threadData.objectFactoryThreadQueue()) {
|
||||||
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
|
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
|
||||||
}
|
}
|
||||||
for (auto node : mm::GlobalData::Instance().objectFactory().Iter()) {
|
for (auto node : mm::GlobalData::Instance().objectFactory().LockForIter()) {
|
||||||
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
|
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
|
||||||
}
|
}
|
||||||
return objects;
|
return objects;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public:
|
|||||||
|
|
||||||
// Lock `MultiSourceQueue` for safe iteration. If element was scheduled for deletion,
|
// Lock `MultiSourceQueue` for safe iteration. If element was scheduled for deletion,
|
||||||
// it'll still be iterated. Use `ApplyDeletions` to remove those elements.
|
// it'll still be iterated. Use `ApplyDeletions` to remove those elements.
|
||||||
Iterable Iter() noexcept { return Iterable(*this); }
|
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||||
|
|
||||||
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
|
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
|
||||||
void ApplyDeletions() noexcept {
|
void ApplyDeletions() noexcept {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
KStdVector<T> Collect(MultiSourceQueue<T>& queue) {
|
KStdVector<T> Collect(MultiSourceQueue<T>& queue) {
|
||||||
KStdVector<T> result;
|
KStdVector<T> result;
|
||||||
for (const auto& element : queue.Iter()) {
|
for (const auto& element : queue.LockForIter()) {
|
||||||
result.push_back(element);
|
result.push_back(element);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -254,7 +254,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
|
|||||||
|
|
||||||
KStdVector<int> actualBefore;
|
KStdVector<int> actualBefore;
|
||||||
{
|
{
|
||||||
auto iter = queue.Iter();
|
auto iter = queue.LockForIter();
|
||||||
while (readyCount < kThreadCount) {
|
while (readyCount < kThreadCount) {
|
||||||
}
|
}
|
||||||
canStart = true;
|
canStart = true;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public:
|
|||||||
|
|
||||||
class Iterable : private MoveOnly {
|
class Iterable : private MoveOnly {
|
||||||
public:
|
public:
|
||||||
explicit Iterable(SingleLockList* list) noexcept : list_(list), guard_(list->mutex_) {}
|
explicit Iterable(SingleLockList* list) noexcept : list_(list), guard_(list->Lock()) {}
|
||||||
|
|
||||||
Iterator begin() noexcept { return Iterator(list_->root_.get()); }
|
Iterator begin() noexcept { return Iterator(list_->root_.get()); }
|
||||||
|
|
||||||
@@ -136,12 +136,14 @@ public:
|
|||||||
|
|
||||||
// Returned value locks `this` to perform safe iteration. `this` unlocks when
|
// Returned value locks `this` to perform safe iteration. `this` unlocks when
|
||||||
// `Iterable` gets out of scope. Example usage:
|
// `Iterable` gets out of scope. Example usage:
|
||||||
// for (auto& value: list.Iter()) {
|
// for (auto& value: list.LockForIter()) {
|
||||||
// // Do something with `value`, there's a guarantee that it'll not be
|
// // Do something with `value`, there's a guarantee that it'll not be
|
||||||
// // destroyed mid-iteration.
|
// // destroyed mid-iteration.
|
||||||
// }
|
// }
|
||||||
// // At this point `list` is unlocked.
|
// // At this point `list` is unlocked.
|
||||||
Iterable Iter() noexcept { return Iterable(this); }
|
Iterable LockForIter() noexcept { return Iterable(this); }
|
||||||
|
|
||||||
|
std::unique_lock<Mutex> Lock() noexcept { return std::unique_lock(mutex_); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Expects `mutex_` to be held by the current thread.
|
// Expects `mutex_` to be held by the current thread.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ TEST(SingleLockListTest, EmplaceAndIter) {
|
|||||||
list.Emplace(kThird);
|
list.Emplace(kThird);
|
||||||
|
|
||||||
KStdVector<int> actual;
|
KStdVector<int> actual;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actual.push_back(element);
|
actual.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ TEST(SingleLockListTest, EmplaceEraseAndIter) {
|
|||||||
list.Erase(secondNode);
|
list.Erase(secondNode);
|
||||||
|
|
||||||
KStdVector<int> actual;
|
KStdVector<int> actual;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actual.push_back(element);
|
actual.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ TEST(SingleLockListTest, IterEmpty) {
|
|||||||
IntList list;
|
IntList list;
|
||||||
|
|
||||||
KStdVector<int> actual;
|
KStdVector<int> actual;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actual.push_back(element);
|
actual.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ TEST(SingleLockListTest, EraseToEmptyEmplaceAndIter) {
|
|||||||
list.Emplace(kFourth);
|
list.Emplace(kFourth);
|
||||||
|
|
||||||
KStdVector<int> actual;
|
KStdVector<int> actual;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actual.push_back(element);
|
actual.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KStdVector<int> actual;
|
KStdVector<int> actual;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actual.push_back(element);
|
actual.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ TEST(SingleLockListTest, ConcurrentErase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KStdVector<int> actual;
|
KStdVector<int> actual;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actual.push_back(element);
|
actual.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
|||||||
|
|
||||||
KStdVector<int> actualBefore;
|
KStdVector<int> actualBefore;
|
||||||
{
|
{
|
||||||
auto iter = list.Iter();
|
auto iter = list.LockForIter();
|
||||||
canStart = true;
|
canStart = true;
|
||||||
while (startedCount < kThreadCount) {
|
while (startedCount < kThreadCount) {
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
|||||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||||
|
|
||||||
KStdVector<int> actualAfter;
|
KStdVector<int> actualAfter;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actualAfter.push_back(element);
|
actualAfter.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
|||||||
|
|
||||||
KStdVector<int> actualBefore;
|
KStdVector<int> actualBefore;
|
||||||
{
|
{
|
||||||
auto iter = list.Iter();
|
auto iter = list.LockForIter();
|
||||||
canStart = true;
|
canStart = true;
|
||||||
while (startedCount < kThreadCount) {
|
while (startedCount < kThreadCount) {
|
||||||
}
|
}
|
||||||
@@ -268,13 +268,99 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
|||||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||||
|
|
||||||
KStdVector<int> actualAfter;
|
KStdVector<int> actualAfter;
|
||||||
for (int element : list.Iter()) {
|
for (int element : list.LockForIter()) {
|
||||||
actualAfter.push_back(element);
|
actualAfter.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_THAT(actualAfter, testing::IsEmpty());
|
EXPECT_THAT(actualAfter, testing::IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SingleLockListTest, LockAndEmplace) {
|
||||||
|
SingleLockList<int, std::recursive_mutex> list;
|
||||||
|
constexpr int kThreadCount = kDefaultThreadCount;
|
||||||
|
|
||||||
|
KStdVector<std::thread> threads;
|
||||||
|
KStdVector<int> actualLocked;
|
||||||
|
KStdVector<int> actualUnlocked;
|
||||||
|
KStdVector<int> expectedUnlocked;
|
||||||
|
for (int i = 0; i < kThreadCount; i++) {
|
||||||
|
expectedUnlocked.push_back(i);
|
||||||
|
}
|
||||||
|
std::atomic<int> startedCount(0);
|
||||||
|
{
|
||||||
|
std::unique_lock lock = list.Lock();
|
||||||
|
for (int i = 0; i < kThreadCount; i++) {
|
||||||
|
threads.emplace_back([&startedCount, &list, i]() {
|
||||||
|
startedCount++;
|
||||||
|
list.Emplace(i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
while (startedCount != kThreadCount) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
// Here still may be a race leading to false-successful EXPECT
|
||||||
|
// if the scheduler suspend all threads right before list.Emplace.
|
||||||
|
// But this situation looks unlikely
|
||||||
|
for (int element : list.LockForIter()) {
|
||||||
|
actualLocked.push_back(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
for (int element : list.LockForIter()) {
|
||||||
|
actualUnlocked.push_back(element);
|
||||||
|
}
|
||||||
|
EXPECT_THAT(actualLocked, testing::IsEmpty());
|
||||||
|
EXPECT_THAT(actualUnlocked, testing::UnorderedElementsAreArray(expectedUnlocked));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SingleLockListTest, LockAndErase) {
|
||||||
|
SingleLockList<int, std::recursive_mutex> list;
|
||||||
|
constexpr int kThreadCount = kDefaultThreadCount;
|
||||||
|
|
||||||
|
KStdVector<SingleLockList<int, std::recursive_mutex>::Node*> items;
|
||||||
|
KStdVector<int> expectedLocked;
|
||||||
|
KStdVector<std::thread> threads;
|
||||||
|
KStdVector<int> actualLocked;
|
||||||
|
KStdVector<int> actualUnlocked;
|
||||||
|
std::atomic<int> startedCount(0);
|
||||||
|
|
||||||
|
for (int i = 0; i < kThreadCount; i++) {
|
||||||
|
expectedLocked.push_back(i);
|
||||||
|
items.push_back(list.Emplace(i));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::unique_lock lock = list.Lock();
|
||||||
|
for (int i = 0; i < kThreadCount; i++) {
|
||||||
|
threads.emplace_back([&startedCount, &list, &items, i]() {
|
||||||
|
startedCount++;
|
||||||
|
list.Erase(items[i]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
while (startedCount != kThreadCount) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
// Here still may be a race leading to false-successful EXPECT
|
||||||
|
// if the scheduler suspend all threads right before list.Erase.
|
||||||
|
// But this situation looks unlikely
|
||||||
|
for (int element : list.LockForIter()) {
|
||||||
|
actualLocked.push_back(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
for (int element : list.LockForIter()) {
|
||||||
|
actualUnlocked.push_back(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_THAT(actualLocked, testing::UnorderedElementsAreArray(expectedLocked));
|
||||||
|
EXPECT_THAT(actualUnlocked, testing::IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class PinnedType : private Pinned {
|
class PinnedType : private Pinned {
|
||||||
@@ -300,7 +386,7 @@ TEST(SingleLockListTest, PinnedType) {
|
|||||||
list.Erase(itemNode);
|
list.Erase(itemNode);
|
||||||
|
|
||||||
KStdVector<PinnedType*> actualAfter;
|
KStdVector<PinnedType*> actualAfter;
|
||||||
for (auto& element : list.Iter()) {
|
for (auto& element : list.LockForIter()) {
|
||||||
actualAfter.push_back(&element);
|
actualAfter.push_back(&element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public:
|
|||||||
stableRefs.ProcessThread(&threadData);
|
stableRefs.ProcessThread(&threadData);
|
||||||
stableRefs.ProcessDeletions();
|
stableRefs.ProcessDeletions();
|
||||||
KStdVector<ObjHeader*> result;
|
KStdVector<ObjHeader*> result;
|
||||||
for (const auto& obj : stableRefs.Iter()) {
|
for (const auto& obj : stableRefs.LockForIter()) {
|
||||||
result.push_back(obj);
|
result.push_back(obj);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
// TODO: Iteration over `globals_` will be slow, because it's `KStdList` collected at different times from
|
// TODO: Iteration over `globals_` will be slow, because it's `KStdList` collected at different times from
|
||||||
// different threads, and so the nodes are all over the memory. Use metrics to understand how
|
// different threads, and so the nodes are all over the memory. Use metrics to understand how
|
||||||
// much of a problem is it.
|
// much of a problem is it.
|
||||||
Iterable Iter() noexcept { return globals_.Iter(); }
|
Iterable LockForIter() noexcept { return globals_.LockForIter(); }
|
||||||
|
|
||||||
void ClearForTests() { globals_.ClearForTests(); }
|
void ClearForTests() { globals_.ClearForTests(); }
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lock `ObjectFactoryStorage` for safe iteration.
|
// Lock `ObjectFactoryStorage` for safe iteration.
|
||||||
Iterable Iter() noexcept { return Iterable(*this); }
|
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||||
|
|
||||||
void ClearForTests() {
|
void ClearForTests() {
|
||||||
root_.reset();
|
root_.reset();
|
||||||
@@ -619,7 +619,7 @@ public:
|
|||||||
|
|
||||||
class Iterable {
|
class Iterable {
|
||||||
public:
|
public:
|
||||||
Iterable(ObjectFactory& owner) noexcept : iter_(owner.storage_.Iter()) {}
|
Iterable(ObjectFactory& owner) noexcept : iter_(owner.storage_.LockForIter()) {}
|
||||||
|
|
||||||
Iterator begin() noexcept { return Iterator(iter_.begin()); }
|
Iterator begin() noexcept { return Iterator(iter_.begin()); }
|
||||||
Iterator end() noexcept { return Iterator(iter_.end()); }
|
Iterator end() noexcept { return Iterator(iter_.end()); }
|
||||||
@@ -637,7 +637,8 @@ public:
|
|||||||
ObjectFactory() noexcept = default;
|
ObjectFactory() noexcept = default;
|
||||||
~ObjectFactory() = default;
|
~ObjectFactory() = default;
|
||||||
|
|
||||||
Iterable Iter() noexcept { return Iterable(*this); }
|
// Lock ObjectFactory for safe iteration.
|
||||||
|
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||||
|
|
||||||
void ClearForTests() { storage_.ClearForTests(); }
|
void ClearForTests() { storage_.ClearForTests(); }
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ using Consumer = typename Storage::Consumer;
|
|||||||
template <size_t DataAlignment>
|
template <size_t DataAlignment>
|
||||||
KStdVector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
KStdVector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||||
KStdVector<void*> result;
|
KStdVector<void*> result;
|
||||||
for (auto& node : storage.Iter()) {
|
for (auto& node : storage.LockForIter()) {
|
||||||
result.push_back(node.Data());
|
result.push_back(node.Data());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -48,7 +48,7 @@ KStdVector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
|||||||
template <typename T, size_t DataAlignment>
|
template <typename T, size_t DataAlignment>
|
||||||
KStdVector<T> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
KStdVector<T> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
|
||||||
KStdVector<T> result;
|
KStdVector<T> result;
|
||||||
for (auto& node : storage.Iter()) {
|
for (auto& node : storage.LockForIter()) {
|
||||||
result.push_back(*static_cast<T*>(node.Data()));
|
result.push_back(*static_cast<T*>(node.Data()));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -137,7 +137,7 @@ TEST(ObjectFactoryStorageTest, PublishDifferentTypes) {
|
|||||||
|
|
||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
auto actual = storage.Iter();
|
auto actual = storage.LockForIter();
|
||||||
auto it = actual.begin();
|
auto it = actual.begin();
|
||||||
EXPECT_THAT(it->Data<int>(), 1);
|
EXPECT_THAT(it->Data<int>(), 1);
|
||||||
++it;
|
++it;
|
||||||
@@ -222,7 +222,7 @@ TEST(ObjectFactoryStorageTest, EraseFirst) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->Data<int>() == 1) {
|
if (it->Data<int>() == 1) {
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
@@ -248,7 +248,7 @@ TEST(ObjectFactoryStorageTest, EraseMiddle) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->Data<int>() == 2) {
|
if (it->Data<int>() == 2) {
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
@@ -274,7 +274,7 @@ TEST(ObjectFactoryStorageTest, EraseLast) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->Data<int>() == 3) {
|
if (it->Data<int>() == 3) {
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
@@ -300,7 +300,7 @@ TEST(ObjectFactoryStorageTest, EraseAll) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
}
|
}
|
||||||
@@ -320,7 +320,7 @@ TEST(ObjectFactoryStorageTest, EraseTheOnlyElement) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
auto it = iter.begin();
|
auto it = iter.begin();
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
EXPECT_THAT(it, iter.end());
|
EXPECT_THAT(it, iter.end());
|
||||||
@@ -343,7 +343,7 @@ TEST(ObjectFactoryStorageTest, MoveFirst) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->Data<int>() == 1) {
|
if (it->Data<int>() == 1) {
|
||||||
iter.MoveAndAdvance(consumer, it);
|
iter.MoveAndAdvance(consumer, it);
|
||||||
@@ -372,7 +372,7 @@ TEST(ObjectFactoryStorageTest, MoveMiddle) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->Data<int>() == 2) {
|
if (it->Data<int>() == 2) {
|
||||||
iter.MoveAndAdvance(consumer, it);
|
iter.MoveAndAdvance(consumer, it);
|
||||||
@@ -401,7 +401,7 @@ TEST(ObjectFactoryStorageTest, MoveLast) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->Data<int>() == 3) {
|
if (it->Data<int>() == 3) {
|
||||||
iter.MoveAndAdvance(consumer, it);
|
iter.MoveAndAdvance(consumer, it);
|
||||||
@@ -430,7 +430,7 @@ TEST(ObjectFactoryStorageTest, MoveAll) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
iter.MoveAndAdvance(consumer, it);
|
iter.MoveAndAdvance(consumer, it);
|
||||||
}
|
}
|
||||||
@@ -453,7 +453,7 @@ TEST(ObjectFactoryStorageTest, MoveTheOnlyElement) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
auto it = iter.begin();
|
auto it = iter.begin();
|
||||||
iter.MoveAndAdvance(consumer, it);
|
iter.MoveAndAdvance(consumer, it);
|
||||||
EXPECT_THAT(it, iter.end());
|
EXPECT_THAT(it, iter.end());
|
||||||
@@ -484,7 +484,7 @@ TEST(ObjectFactoryStorageTest, MoveAndErase) {
|
|||||||
producer.Publish();
|
producer.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
++it;
|
++it;
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
@@ -566,7 +566,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
|
|||||||
|
|
||||||
KStdVector<int> actualBefore;
|
KStdVector<int> actualBefore;
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
while (readyCount < kThreadCount) {
|
while (readyCount < kThreadCount) {
|
||||||
}
|
}
|
||||||
canStart = true;
|
canStart = true;
|
||||||
@@ -624,7 +624,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = storage.Iter();
|
auto iter = storage.LockForIter();
|
||||||
while (readyCount < kThreadCount) {
|
while (readyCount < kThreadCount) {
|
||||||
}
|
}
|
||||||
canStart = true;
|
canStart = true;
|
||||||
@@ -772,7 +772,7 @@ TEST(ObjectFactoryTest, CreateObject) {
|
|||||||
EXPECT_THAT(node.GetObjHeader(), object);
|
EXPECT_THAT(node.GetObjHeader(), object);
|
||||||
EXPECT_THAT(node.GCObjectData().flags, 42);
|
EXPECT_THAT(node.GCObjectData().flags, 42);
|
||||||
|
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
auto it = iter.begin();
|
auto it = iter.begin();
|
||||||
EXPECT_THAT(*it, node);
|
EXPECT_THAT(*it, node);
|
||||||
++it;
|
++it;
|
||||||
@@ -792,7 +792,7 @@ TEST(ObjectFactoryTest, CreateObjectArray) {
|
|||||||
EXPECT_THAT(node.GetArrayHeader(), array);
|
EXPECT_THAT(node.GetArrayHeader(), array);
|
||||||
EXPECT_THAT(node.GCObjectData().flags, 42);
|
EXPECT_THAT(node.GCObjectData().flags, 42);
|
||||||
|
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
auto it = iter.begin();
|
auto it = iter.begin();
|
||||||
EXPECT_THAT(*it, node);
|
EXPECT_THAT(*it, node);
|
||||||
++it;
|
++it;
|
||||||
@@ -812,7 +812,7 @@ TEST(ObjectFactoryTest, CreateCharArray) {
|
|||||||
EXPECT_THAT(node.GetArrayHeader(), array);
|
EXPECT_THAT(node.GetArrayHeader(), array);
|
||||||
EXPECT_THAT(node.GCObjectData().flags, 42);
|
EXPECT_THAT(node.GCObjectData().flags, 42);
|
||||||
|
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
auto it = iter.begin();
|
auto it = iter.begin();
|
||||||
EXPECT_THAT(*it, node);
|
EXPECT_THAT(*it, node);
|
||||||
++it;
|
++it;
|
||||||
@@ -833,7 +833,7 @@ TEST(ObjectFactoryTest, Erase) {
|
|||||||
threadQueue.Publish();
|
threadQueue.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->IsArray()) {
|
if (it->IsArray()) {
|
||||||
iter.EraseAndAdvance(it);
|
iter.EraseAndAdvance(it);
|
||||||
@@ -844,7 +844,7 @@ TEST(ObjectFactoryTest, Erase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (auto it = iter.begin(); it != iter.end(); ++it, ++count) {
|
for (auto it = iter.begin(); it != iter.end(); ++it, ++count) {
|
||||||
EXPECT_FALSE(it->IsArray());
|
EXPECT_FALSE(it->IsArray());
|
||||||
@@ -868,7 +868,7 @@ TEST(ObjectFactoryTest, Move) {
|
|||||||
threadQueue.Publish();
|
threadQueue.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
if (it->IsArray()) {
|
if (it->IsArray()) {
|
||||||
iter.MoveAndAdvance(finalizerQueue, it);
|
iter.MoveAndAdvance(finalizerQueue, it);
|
||||||
@@ -879,7 +879,7 @@ TEST(ObjectFactoryTest, Move) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (auto it = iter.begin(); it != iter.end(); ++it, ++count) {
|
for (auto it = iter.begin(); it != iter.end(); ++it, ++count) {
|
||||||
EXPECT_FALSE(it->IsArray());
|
EXPECT_FALSE(it->IsArray());
|
||||||
@@ -914,7 +914,7 @@ TEST(ObjectFactoryTest, RunFinalizers) {
|
|||||||
threadQueue.Publish();
|
threadQueue.Publish();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
for (auto it = iter.begin(); it != iter.end();) {
|
for (auto it = iter.begin(); it != iter.end();) {
|
||||||
iter.MoveAndAdvance(finalizerQueue, it);
|
iter.MoveAndAdvance(finalizerQueue, it);
|
||||||
}
|
}
|
||||||
@@ -961,7 +961,7 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
|
|||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iter = objectFactory.Iter();
|
auto iter = objectFactory.LockForIter();
|
||||||
KStdVector<ObjHeader*> actual;
|
KStdVector<ObjHeader*> actual;
|
||||||
for (auto it = iter.begin(); it != iter.end(); ++it) {
|
for (auto it = iter.begin(); it != iter.end(); ++it) {
|
||||||
actual.push_back(it->GetObjHeader());
|
actual.push_back(it->GetObjHeader());
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
GlobalRootSet(GlobalsRegistry& globalsRegistry, StableRefRegistry& stableRefRegistry) noexcept :
|
GlobalRootSet(GlobalsRegistry& globalsRegistry, StableRefRegistry& stableRefRegistry) noexcept :
|
||||||
globalsIterable_(globalsRegistry.Iter()), stableRefsIterable_(stableRefRegistry.Iter()) {}
|
globalsIterable_(globalsRegistry.LockForIter()), stableRefsIterable_(stableRefRegistry.LockForIter()) {}
|
||||||
GlobalRootSet() noexcept;
|
GlobalRootSet() noexcept;
|
||||||
|
|
||||||
Iterator begin() noexcept { return Iterator(Iterator::begin, *this); }
|
Iterator begin() noexcept { return Iterator(Iterator::begin, *this); }
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ public:
|
|||||||
// when it's asked by GC to stop.
|
// when it's asked by GC to stop.
|
||||||
void ProcessThread(mm::ThreadData* threadData) noexcept;
|
void ProcessThread(mm::ThreadData* threadData) noexcept;
|
||||||
|
|
||||||
// Lock registry and apply deletions. Should be called on GC thread after all threads have published, and before `Iter`.
|
// Lock registry and apply deletions. Should be called on GC thread after all threads have published, and before `LockForIter`.
|
||||||
void ProcessDeletions() noexcept;
|
void ProcessDeletions() noexcept;
|
||||||
|
|
||||||
// Lock registry for safe iteration.
|
// Lock registry for safe iteration.
|
||||||
// TODO: Iteration over `stableRefs_` will be slow, because it's `KStdList` collected at different times from
|
// TODO: Iteration over `stableRefs_` will be slow, because it's `KStdList` collected at different times from
|
||||||
// different threads, and so the nodes are all over the memory. Use metrics to understand how
|
// different threads, and so the nodes are all over the memory. Use metrics to understand how
|
||||||
// much of a problem is it.
|
// much of a problem is it.
|
||||||
Iterable Iter() noexcept { return stableRefs_.Iter(); }
|
Iterable LockForIter() noexcept { return stableRefs_.LockForIter(); }
|
||||||
|
|
||||||
void ClearForTests() noexcept { stableRefs_.ClearForTests(); }
|
void ClearForTests() noexcept { stableRefs_.ClearForTests(); }
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ std::vector<mm::ThreadData*> collect(mm::ThreadRegistry::Iterable& iterable) {
|
|||||||
|
|
||||||
extern "C" void Kotlin_TestSupport_AssertClearGlobalState() {
|
extern "C" void Kotlin_TestSupport_AssertClearGlobalState() {
|
||||||
// Validate that global registries are empty.
|
// Validate that global registries are empty.
|
||||||
auto globals = mm::GlobalsRegistry::Instance().Iter();
|
auto globals = mm::GlobalsRegistry::Instance().LockForIter();
|
||||||
auto objects = mm::GlobalData::Instance().objectFactory().Iter();
|
auto objects = mm::GlobalData::Instance().objectFactory().LockForIter();
|
||||||
auto stableRefs = mm::StableRefRegistry::Instance().Iter();
|
auto stableRefs = mm::StableRefRegistry::Instance().LockForIter();
|
||||||
auto threads = mm::ThreadRegistry::Instance().Iter();
|
auto threads = mm::ThreadRegistry::Instance().LockForIter();
|
||||||
|
|
||||||
EXPECT_THAT(collect<ObjHeader**>(globals), testing::UnorderedElementsAre());
|
EXPECT_THAT(collect<ObjHeader**>(globals), testing::UnorderedElementsAre());
|
||||||
EXPECT_THAT(collect<mm::ObjectFactory<gc::GC>::NodeRef>(objects), testing::UnorderedElementsAre());
|
EXPECT_THAT(collect<mm::ObjectFactory<gc::GC>::NodeRef>(objects), testing::UnorderedElementsAre());
|
||||||
|
|||||||
@@ -31,8 +31,12 @@ void mm::ThreadRegistry::Unregister(Node* threadDataNode) noexcept {
|
|||||||
// Do not touch `currentThreadData_` as TLS may already have been deallocated.
|
// Do not touch `currentThreadData_` as TLS may already have been deallocated.
|
||||||
}
|
}
|
||||||
|
|
||||||
mm::ThreadRegistry::Iterable mm::ThreadRegistry::Iter() noexcept {
|
mm::ThreadRegistry::Iterable mm::ThreadRegistry::LockForIter() noexcept {
|
||||||
return list_.Iter();
|
return list_.LockForIter();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<mm::ThreadRegistry::Mutex> mm::ThreadRegistry::Lock() noexcept {
|
||||||
|
return list_.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE mm::ThreadData* mm::ThreadRegistry::CurrentThreadData() const noexcept {
|
ALWAYS_INLINE mm::ThreadData* mm::ThreadRegistry::CurrentThreadData() const noexcept {
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ class ThreadData;
|
|||||||
|
|
||||||
class ThreadRegistry final : private Pinned {
|
class ThreadRegistry final : private Pinned {
|
||||||
public:
|
public:
|
||||||
using Node = SingleLockList<ThreadData>::Node;
|
using Mutex = std::recursive_mutex;
|
||||||
using Iterable = SingleLockList<ThreadData>::Iterable;
|
using Node = SingleLockList<ThreadData, Mutex>::Node;
|
||||||
|
using Iterable = SingleLockList<ThreadData, Mutex>::Iterable;
|
||||||
|
|
||||||
static ThreadRegistry& Instance() noexcept;
|
static ThreadRegistry& Instance() noexcept;
|
||||||
|
|
||||||
@@ -30,7 +31,9 @@ public:
|
|||||||
void Unregister(Node* threadDataNode) noexcept;
|
void Unregister(Node* threadDataNode) noexcept;
|
||||||
|
|
||||||
// Locks `ThreadRegistry` for safe iteration.
|
// Locks `ThreadRegistry` for safe iteration.
|
||||||
Iterable Iter() noexcept;
|
Iterable LockForIter() noexcept;
|
||||||
|
|
||||||
|
std::unique_lock<Mutex> Lock() noexcept;
|
||||||
|
|
||||||
// Try not to use these methods very often, as (1) thread local access can be slow on some platforms,
|
// Try not to use these methods very often, as (1) thread local access can be slow on some platforms,
|
||||||
// (2) TLS gets deallocated before our thread destruction hooks run.
|
// (2) TLS gets deallocated before our thread destruction hooks run.
|
||||||
@@ -51,7 +54,7 @@ private:
|
|||||||
|
|
||||||
static THREAD_LOCAL_VARIABLE Node* currentThreadDataNode_;
|
static THREAD_LOCAL_VARIABLE Node* currentThreadDataNode_;
|
||||||
|
|
||||||
SingleLockList<ThreadData> list_;
|
SingleLockList<ThreadData, Mutex> list_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mm
|
} // namespace mm
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ template<typename F>
|
|||||||
bool allThreads(F predicate) noexcept {
|
bool allThreads(F predicate) noexcept {
|
||||||
auto& threadRegistry = kotlin::mm::ThreadRegistry::Instance();
|
auto& threadRegistry = kotlin::mm::ThreadRegistry::Instance();
|
||||||
auto* currentThread = threadRegistry.CurrentThreadData();
|
auto* currentThread = threadRegistry.CurrentThreadData();
|
||||||
kotlin::mm::ThreadRegistry::Iterable threads = kotlin::mm::ThreadRegistry::Instance().Iter();
|
kotlin::mm::ThreadRegistry::Iterable threads = kotlin::mm::ThreadRegistry::Instance().LockForIter();
|
||||||
for (auto& thread : threads) {
|
for (auto& thread : threads) {
|
||||||
// Handle if suspension was initiated by the mutator thread.
|
// Handle if suspension was initiated by the mutator thread.
|
||||||
if (&thread == currentThread)
|
if (&thread == currentThread)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ constexpr size_t kDefaultReportingStep = 1000;
|
|||||||
|
|
||||||
KStdVector<mm::ThreadData*> collectThreadData() {
|
KStdVector<mm::ThreadData*> collectThreadData() {
|
||||||
KStdVector<mm::ThreadData*> result;
|
KStdVector<mm::ThreadData*> result;
|
||||||
auto iter = mm::ThreadRegistry::Instance().Iter();
|
auto iter = mm::ThreadRegistry::Instance().LockForIter();
|
||||||
for (auto& thread : iter) {
|
for (auto& thread : iter) {
|
||||||
result.push_back(&thread);
|
result.push_back(&thread);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user