[K/N] Add ability to get base object from extra data

^KT-49325
This commit is contained in:
Pavel Kunyavskiy
2021-10-18 14:32:48 +03:00
committed by Space
parent da3e31cd3a
commit ae64af09fb
15 changed files with 118 additions and 51 deletions
@@ -40,7 +40,7 @@ void Mark(KStdVector<ObjHeader*> graySet) noexcept {
} }
if (auto* extraObjectData = mm::ExtraObjectData::Get(top)) { if (auto* extraObjectData = mm::ExtraObjectData::Get(top)) {
auto* weakCounter = *extraObjectData->GetWeakCounterLocation(); auto weakCounter = extraObjectData->GetWeakReferenceCounter();
if (!isNullOrMarker(weakCounter)) { if (!isNullOrMarker(weakCounter)) {
graySet.push_back(weakCounter); graySet.push_back(weakCounter);
} }
@@ -48,7 +48,8 @@ public:
void InstallWeakCounter(BaseObject& counter) { void InstallWeakCounter(BaseObject& counter) {
auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(GetObjHeader()); auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(GetObjHeader());
*extraObjectData.GetWeakCounterLocation() = counter.GetObjHeader(); auto *setCounter = extraObjectData.GetOrSetWeakReferenceCounter(GetObjHeader(), counter.GetObjHeader());
EXPECT_EQ(setCounter, counter.GetObjHeader());
} }
protected: protected:
@@ -225,7 +225,8 @@ public:
objectFactoryThreadQueue_.Publish(); objectFactoryThreadQueue_.Publish();
auto& weakCounter = WeakCounter::FromObjHeader(weakCounterHeader); auto& weakCounter = WeakCounter::FromObjHeader(weakCounterHeader);
auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(objHeader); auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(objHeader);
*extraObjectData.GetWeakCounterLocation() = weakCounter.header(); auto *setHeader = extraObjectData.GetOrSetWeakReferenceCounter(objHeader, weakCounter.header());
EXPECT_EQ(setHeader, weakCounter.header());
weakCounter->referred = objHeader; weakCounter->referred = objHeader;
return weakCounter; return weakCounter;
} }
@@ -208,7 +208,8 @@ WeakCounter& InstallWeakCounter(mm::ThreadData& threadData, ObjHeader* objHeader
mm::AllocateObject(&threadData, typeHolderWeakCounter.typeInfo(), location); mm::AllocateObject(&threadData, typeHolderWeakCounter.typeInfo(), location);
auto& weakCounter = WeakCounter::FromObjHeader(*location); auto& weakCounter = WeakCounter::FromObjHeader(*location);
auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(objHeader); auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(objHeader);
*extraObjectData.GetWeakCounterLocation() = weakCounter.header(); auto *setCounter = extraObjectData.GetOrSetWeakReferenceCounter(objHeader, weakCounter.header());
EXPECT_EQ(setCounter, weakCounter.header());
weakCounter->referred = objHeader; weakCounter->referred = objHeader;
return weakCounter; return weakCounter;
} }
@@ -222,6 +223,7 @@ public:
~SameThreadMarkAndSweepTest() { ~SameThreadMarkAndSweepTest() {
mm::GlobalsRegistry::Instance().ClearForTests(); mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests(); mm::GlobalData::Instance().objectFactory().ClearForTests();
mm::GlobalData::Instance().gcScheduler().ReplaceGCSchedulerDataForTests( mm::GlobalData::Instance().gcScheduler().ReplaceGCSchedulerDataForTests(
[](auto& config, auto scheduleGC) { return gc::internal::MakeGCSchedulerData(config, std::move(scheduleGC)); }); [](auto& config, auto scheduleGC) { return gc::internal::MakeGCSchedulerData(config, std::move(scheduleGC)); });
@@ -477,8 +477,13 @@ ALWAYS_INLINE bool isShareable(const ObjHeader* obj) {
return containerFor(obj)->shareable(); return containerFor(obj)->shareable();
} }
ObjHeader** ObjHeader::GetWeakCounterLocation() { ObjHeader* ObjHeader::GetWeakCounter() {
return &this->meta_object()->WeakReference.counter_; return this->meta_object()->WeakReference.counter_;
}
ObjHeader* ObjHeader::GetOrSetWeakCounter(ObjHeader* counter) {
UpdateHeapRefIfNull(&meta_object()->WeakReference.counter_, counter);
return GetWeakCounter();
} }
#if KONAN_OBJC_INTEROP #if KONAN_OBJC_INTEROP
+3 -1
View File
@@ -68,7 +68,9 @@ struct ObjHeader {
MetaObjHeader* meta_object_or_null() const noexcept { return AsMetaObject(typeInfoOrMeta_); } MetaObjHeader* meta_object_or_null() const noexcept { return AsMetaObject(typeInfoOrMeta_); }
ALWAYS_INLINE ObjHeader** GetWeakCounterLocation(); ALWAYS_INLINE ObjHeader* GetWeakCounter();
ALWAYS_INLINE ObjHeader* GetOrSetWeakCounter(ObjHeader* counter);
#ifdef KONAN_OBJC_INTEROP #ifdef KONAN_OBJC_INTEROP
ALWAYS_INLINE void* GetAssociatedObject(); ALWAYS_INLINE void* GetAssociatedObject();
+8 -4
View File
@@ -68,14 +68,14 @@ OBJ_GETTER(Konan_getWeakReferenceImpl, ObjHeader* referred) {
} }
#endif // KONAN_OBJC_INTEROP #endif // KONAN_OBJC_INTEROP
ObjHeader** weakCounterLocation = referred->GetWeakCounterLocation(); ObjHeader* weakCounter = referred->GetWeakCounter();
if (*weakCounterLocation == nullptr) { if (weakCounter == nullptr) {
ObjHolder counterHolder; ObjHolder counterHolder;
// Cast unneeded, just to emphasize we store an object reference as void*. // Cast unneeded, just to emphasize we store an object reference as void*.
ObjHeader* counter = makeWeakReferenceCounter(reinterpret_cast<void*>(referred), counterHolder.slot()); ObjHeader* counter = makeWeakReferenceCounter(reinterpret_cast<void*>(referred), counterHolder.slot());
UpdateHeapRefIfNull(weakCounterLocation, counter); weakCounter = referred->GetOrSetWeakCounter(counter);
} }
RETURN_OBJ(*weakCounterLocation); RETURN_OBJ(weakCounter);
} }
// Materialize a weak reference to either null or the real reference. // Materialize a weak reference to either null or the real reference.
@@ -89,6 +89,10 @@ OBJ_GETTER(Konan_WeakReferenceCounter_get, ObjHeader* counter) {
#endif #endif
} }
ALWAYS_INLINE ObjHeader* UnsafeWeakReferenceCounterGet(ObjHeader* counter) {
return asWeakReferenceCounter(counter)->referred;
}
void WeakReferenceCounterClear(ObjHeader* counter) { void WeakReferenceCounterClear(ObjHeader* counter) {
ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred; ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred;
// Note, that we don't do UpdateRef here, as reference is weak. // Note, that we don't do UpdateRef here, as reference is weak.
@@ -12,6 +12,7 @@ extern "C" {
// Atomically clears counter object reference. // Atomically clears counter object reference.
void WeakReferenceCounterClear(ObjHeader* counter); void WeakReferenceCounterClear(ObjHeader* counter);
ObjHeader* UnsafeWeakReferenceCounterGet(ObjHeader* counter);
} // extern "C" } // extern "C"
@@ -44,7 +44,7 @@ mm::ExtraObjectData& mm::ExtraObjectData::Install(ObjHeader* object) noexcept {
RuntimeCheck(!hasPointerBits(typeInfo, OBJECT_TAG_MASK), "Object must not be tagged"); RuntimeCheck(!hasPointerBits(typeInfo, OBJECT_TAG_MASK), "Object must not be tagged");
auto *threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); auto *threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
auto& data = mm::ExtraObjectDataFactory::Instance().CreateExtraObjectDataForObject(threadData, typeInfo); auto& data = mm::ExtraObjectDataFactory::Instance().CreateExtraObjectDataForObject(threadData, object, typeInfo);
TypeInfo* old = __sync_val_compare_and_swap(&object->typeInfoOrMeta_, typeInfo, reinterpret_cast<TypeInfo*>(&data)); TypeInfo* old = __sync_val_compare_and_swap(&object->typeInfoOrMeta_, typeInfo, reinterpret_cast<TypeInfo*>(&data));
if (old != typeInfo) { if (old != typeInfo) {
@@ -78,18 +78,15 @@ void mm::ExtraObjectData::DetachAssociatedObject() noexcept {
#endif #endif
} }
bool mm::ExtraObjectData::HasWeakReferenceCounter() noexcept {
return weakReferenceCounter_ != nullptr;
}
void mm::ExtraObjectData::ClearWeakReferenceCounter() noexcept { void mm::ExtraObjectData::ClearWeakReferenceCounter() noexcept {
if (!HasWeakReferenceCounter()) return; if (!HasWeakReferenceCounter()) return;
WeakReferenceCounterClear(weakReferenceCounter_); auto *object = GetBaseObject();
WeakReferenceCounterClear(GetWeakReferenceCounter());
// Not using `mm::SetHeapRef here`, because this code is called during sweep phase by the GC thread, // Not using `mm::SetHeapRef here`, because this code is called during sweep phase by the GC thread,
// and so cannot affect marking. // and so cannot affect marking.
// TODO: Asserts on the above? // TODO: Asserts on the above?
weakReferenceCounter_ = nullptr; weakReferenceCounterOrBaseObject_ = object;
} }
mm::ExtraObjectData::~ExtraObjectData() { mm::ExtraObjectData::~ExtraObjectData() {
@@ -15,6 +15,7 @@
#include "TypeInfo.h" #include "TypeInfo.h"
#include "Utils.hpp" #include "Utils.hpp"
#include "MultiSourceQueue.hpp" #include "MultiSourceQueue.hpp"
#include "Weak.h"
namespace kotlin { namespace kotlin {
namespace mm { namespace mm {
@@ -28,6 +29,8 @@ public:
FLAGS_NEVER_FROZEN = 1 << 1, FLAGS_NEVER_FROZEN = 1 << 1,
}; };
static constexpr unsigned WEAK_REF_TAG = 1;
MetaObjHeader* AsMetaObjHeader() noexcept { return reinterpret_cast<MetaObjHeader*>(this); } MetaObjHeader* AsMetaObjHeader() noexcept { return reinterpret_cast<MetaObjHeader*>(this); }
static ExtraObjectData& FromMetaObjHeader(MetaObjHeader* header) noexcept { return *reinterpret_cast<ExtraObjectData*>(header); } static ExtraObjectData& FromMetaObjHeader(MetaObjHeader* header) noexcept { return *reinterpret_cast<ExtraObjectData*>(header); }
@@ -45,14 +48,35 @@ public:
#endif #endif
void DetachAssociatedObject() noexcept; void DetachAssociatedObject() noexcept;
ObjHeader** GetWeakCounterLocation() noexcept { return &weakReferenceCounter_; }
std::atomic<Flags>& flags() noexcept { return flags_; } std::atomic<Flags>& flags() noexcept { return flags_; }
bool HasWeakReferenceCounter() noexcept; bool HasWeakReferenceCounter() noexcept { return hasPointerBits(weakReferenceCounterOrBaseObject_.load(), WEAK_REF_TAG); }
void ClearWeakReferenceCounter() noexcept; void ClearWeakReferenceCounter() noexcept;
ObjHeader* GetWeakReferenceCounter() noexcept {
auto *pointer = weakReferenceCounterOrBaseObject_.load();
if (hasPointerBits(pointer, WEAK_REF_TAG)) return clearPointerBits(pointer, WEAK_REF_TAG);
return nullptr;
}
ObjHeader* GetOrSetWeakReferenceCounter(ObjHeader* object, ObjHeader* counter) noexcept {
if (weakReferenceCounterOrBaseObject_.compare_exchange_strong(object, setPointerBits(counter, WEAK_REF_TAG))) {
return counter;
} else {
return clearPointerBits(object, WEAK_REF_TAG); // on fail current value of counter is stored to object
}
}
ObjHeader* GetBaseObject() noexcept {
auto *header = weakReferenceCounterOrBaseObject_.load();
if (hasPointerBits(header, WEAK_REF_TAG)) {
return UnsafeWeakReferenceCounterGet(clearPointerBits(header, WEAK_REF_TAG));
} else {
return header;
}
}
explicit ExtraObjectData(const TypeInfo* typeInfo) noexcept : typeInfo_(typeInfo) {} // info must be equal to objHeader->type_info(), but it needs to be loaded in advance to avoid data races
explicit ExtraObjectData(ObjHeader* objHeader, const TypeInfo *info) noexcept :
typeInfo_(info), weakReferenceCounterOrBaseObject_(objHeader) {
}
~ExtraObjectData(); ~ExtraObjectData();
private: private:
@@ -65,7 +89,7 @@ private:
void* associatedObject_ = nullptr; void* associatedObject_ = nullptr;
#endif #endif
ObjHeader* weakReferenceCounter_ = nullptr; std::atomic<ObjHeader*> weakReferenceCounterOrBaseObject_;
}; };
} // namespace mm } // namespace mm
@@ -15,12 +15,24 @@ mm::ExtraObjectDataFactory& mm::ExtraObjectDataFactory::Instance() noexcept {
return GlobalData::Instance().extraObjectDataFactory(); return GlobalData::Instance().extraObjectDataFactory();
} }
mm::ExtraObjectData& mm::ExtraObjectDataFactory::CreateExtraObjectDataForObject(mm::ThreadData* threadData, TypeInfo* info) noexcept { mm::ExtraObjectData& mm::ExtraObjectDataFactory::CreateExtraObjectDataForObject(
return **threadData->extraObjectDataThreadQueue().Emplace(info); mm::ThreadData* threadData, ObjHeader* baseObject, const TypeInfo* info
) noexcept {
return CreateExtraObjectDataForObject(threadData->extraObjectDataThreadQueue(), baseObject, info);
} }
void mm::ExtraObjectDataFactory::DestroyExtraObjectData(mm::ThreadData* threadData, ExtraObjectData& data) noexcept { void mm::ExtraObjectDataFactory::DestroyExtraObjectData(mm::ThreadData* threadData, ExtraObjectData& data) noexcept {
threadData->extraObjectDataThreadQueue().Erase(&Queue::Node::fromValue(data)); DestroyExtraObjectData(threadData->extraObjectDataThreadQueue(), data);
}
mm::ExtraObjectData& mm::ExtraObjectDataFactory::CreateExtraObjectDataForObject(
ThreadQueue& threadQueue, ObjHeader* baseObject, const TypeInfo* info
) noexcept {
return **threadQueue.Emplace(baseObject, info);
}
void mm::ExtraObjectDataFactory::DestroyExtraObjectData(ThreadQueue& threadQueue, ExtraObjectData& data) noexcept {
threadQueue.Erase(&Queue::Node::fromValue(data));
} }
void mm::ExtraObjectDataFactory::ProcessThread(mm::ThreadData* threadData) noexcept { void mm::ExtraObjectDataFactory::ProcessThread(mm::ThreadData* threadData) noexcept {
@@ -33,9 +33,11 @@ public:
static ExtraObjectDataFactory& Instance() noexcept; static ExtraObjectDataFactory& Instance() noexcept;
ExtraObjectData& CreateExtraObjectDataForObject(mm::ThreadData* threadData, TypeInfo* info) noexcept; ExtraObjectData& CreateExtraObjectDataForObject(mm::ThreadData* threadData, ObjHeader* baseObject, const TypeInfo* info) noexcept;
ExtraObjectData& CreateExtraObjectDataForObject(ThreadQueue& threadQueue, ObjHeader* baseObject, const TypeInfo* info) noexcept;
void DestroyExtraObjectData(mm::ThreadData* threadData, ExtraObjectData& data) noexcept; void DestroyExtraObjectData(mm::ThreadData* threadData, ExtraObjectData& data) noexcept;
void DestroyExtraObjectData(ThreadQueue& threadQueue, ExtraObjectData& data) noexcept;
// Collect extra data objects from thread corresponding to `threadData`. Must be called by the thread // Collect extra data objects from thread corresponding to `threadData`. Must be called by the thread
// when it's asked by GC to stop. // when it's asked by GC to stop.
@@ -23,9 +23,20 @@ struct EmptyPayload {
static constexpr std::array<Field, 0> kFields{}; static constexpr std::array<Field, 0> kFields{};
}; };
class ExtraObjectDataTest : public testing::Test {
public:
ExtraObjectDataTest() {}
~ExtraObjectDataTest() {
mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests();
}
};
} // namespace } // namespace
TEST(ExtraObjectDataTest, Install) { TEST_F(ExtraObjectDataTest, Install) {
ScopedMemoryInit init; ScopedMemoryInit init;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()}; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo()); test_support::Object<EmptyPayload> object(type.typeInfo());
@@ -38,6 +49,8 @@ TEST(ExtraObjectDataTest, Install) {
EXPECT_TRUE(object.header()->has_meta_object()); EXPECT_TRUE(object.header()->has_meta_object());
EXPECT_THAT(object.header()->meta_object(), extraData.AsMetaObjHeader()); EXPECT_THAT(object.header()->meta_object(), extraData.AsMetaObjHeader());
EXPECT_THAT(object.header()->type_info(), typeInfo); EXPECT_THAT(object.header()->type_info(), typeInfo);
EXPECT_FALSE(extraData.HasWeakReferenceCounter());
EXPECT_THAT(extraData.GetBaseObject(), object.header());
mm::ExtraObjectData::Uninstall(object.header()); mm::ExtraObjectData::Uninstall(object.header());
@@ -45,7 +58,7 @@ TEST(ExtraObjectDataTest, Install) {
EXPECT_THAT(object.header()->type_info(), typeInfo); EXPECT_THAT(object.header()->type_info(), typeInfo);
} }
TEST(ExtraObjectDataTest, ConcurrentInstall) { TEST_F(ExtraObjectDataTest, ConcurrentInstall) {
ScopedMemoryInit init; ScopedMemoryInit init;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()}; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo()); test_support::Object<EmptyPayload> object(type.typeInfo());
@@ -65,13 +78,13 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
} }
auto& extraData = mm::ExtraObjectData::Install(object.header()); auto& extraData = mm::ExtraObjectData::Install(object.header());
actual[i] = &extraData; actual[i] = &extraData;
mm::GlobalData::Instance().threadRegistry().CurrentThreadData()->Publish();
}); });
} }
while (readyCount < kThreadCount) { while (readyCount < kThreadCount) {
} }
canStart = true; canStart = true;
for (auto& t : threads) { for (auto& t : threads) {
t.join(); t.join();
} }
@@ -79,6 +92,4 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
std::vector<mm::ExtraObjectData*> expected(kThreadCount, actual[0]); std::vector<mm::ExtraObjectData*> expected(kThreadCount, actual[0]);
EXPECT_THAT(actual, testing::ElementsAreArray(expected)); EXPECT_THAT(actual, testing::ElementsAreArray(expected));
mm::ExtraObjectData::Uninstall(object.header());
} }
+6 -2
View File
@@ -48,8 +48,12 @@ ALWAYS_INLINE mm::StableRefRegistry::Node* FromForeignRefManager(ForeignRefManag
} // namespace } // namespace
ObjHeader** ObjHeader::GetWeakCounterLocation() { ObjHeader* ObjHeader::GetWeakCounter() {
return mm::ExtraObjectData::FromMetaObjHeader(this->meta_object()).GetWeakCounterLocation(); return mm::ExtraObjectData::FromMetaObjHeader(this->meta_object()).GetWeakReferenceCounter();
}
ObjHeader* ObjHeader::GetOrSetWeakCounter(ObjHeader* counter) {
return mm::ExtraObjectData::FromMetaObjHeader(this->meta_object()).GetOrSetWeakReferenceCounter(this, counter);
} }
#ifdef KONAN_OBJC_INTEROP #ifdef KONAN_OBJC_INTEROP
@@ -22,37 +22,38 @@ bool isEmpty(T& iterable) {
return iterable.begin() == iterable.end(); return iterable.begin() == iterable.end();
} }
template <typename E, typename T> template <typename T>
std::vector<E> collect(T& iterable) { auto collectCopy(T& iterable) {
std::vector<E> result; std::vector<std::remove_reference_t<decltype(*iterable.begin())>> result;
for (E element : iterable) { for (const auto &element : iterable) {
result.push_back(element); result.push_back(element);
} }
return std::move(result);
}
std::vector<mm::ThreadData*> collect(mm::ThreadRegistry::Iterable& iterable) {
std::vector<mm::ThreadData*> result;
for (mm::ThreadData& element : iterable) {
result.push_back(&element);
}
// Do not use std::move because clang complains that it prevents copy elision.
return result; return result;
} }
template <typename T>
auto collectPointers(T& iterable) {
std::vector<const std::remove_reference_t<decltype(*iterable.begin())>*> result;
for (const auto &element : iterable) {
result.push_back(&element);
}
return result;
}
} // namespace } // namespace
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().LockForIter(); auto globals = mm::GlobalsRegistry::Instance().LockForIter();
auto extraObjects = mm::GlobalData::Instance().extraObjectDataFactory().LockForIter();
auto objects = mm::GlobalData::Instance().objectFactory().LockForIter(); auto objects = mm::GlobalData::Instance().objectFactory().LockForIter();
auto stableRefs = mm::StableRefRegistry::Instance().LockForIter(); auto stableRefs = mm::StableRefRegistry::Instance().LockForIter();
auto threads = mm::ThreadRegistry::Instance().LockForIter(); auto threads = mm::ThreadRegistry::Instance().LockForIter();
EXPECT_THAT(collect<ObjHeader**>(globals), testing::UnorderedElementsAre()); EXPECT_THAT(collectCopy(globals), testing::UnorderedElementsAre());
EXPECT_THAT(collect<mm::ObjectFactory<gc::GC>::NodeRef>(objects), testing::UnorderedElementsAre()); EXPECT_THAT(collectPointers(extraObjects), testing::UnorderedElementsAre());
EXPECT_THAT(collect<ObjHeader*>(stableRefs), testing::UnorderedElementsAre()); EXPECT_THAT(collectCopy(objects), testing::UnorderedElementsAre());
EXPECT_THAT(collect(threads), testing::UnorderedElementsAre()); EXPECT_THAT(collectCopy(stableRefs), testing::UnorderedElementsAre());
EXPECT_THAT(collectPointers(threads), testing::UnorderedElementsAre());
} }
void kotlin::DeinitMemoryForTests(MemoryState* memoryState) { void kotlin::DeinitMemoryForTests(MemoryState* memoryState) {