[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)) {
auto* weakCounter = *extraObjectData->GetWeakCounterLocation();
auto weakCounter = extraObjectData->GetWeakReferenceCounter();
if (!isNullOrMarker(weakCounter)) {
graySet.push_back(weakCounter);
}
@@ -48,7 +48,8 @@ public:
void InstallWeakCounter(BaseObject& counter) {
auto& extraObjectData = mm::ExtraObjectData::GetOrInstall(GetObjHeader());
*extraObjectData.GetWeakCounterLocation() = counter.GetObjHeader();
auto *setCounter = extraObjectData.GetOrSetWeakReferenceCounter(GetObjHeader(), counter.GetObjHeader());
EXPECT_EQ(setCounter, counter.GetObjHeader());
}
protected:
@@ -225,7 +225,8 @@ public:
objectFactoryThreadQueue_.Publish();
auto& weakCounter = WeakCounter::FromObjHeader(weakCounterHeader);
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;
return weakCounter;
}
@@ -208,7 +208,8 @@ WeakCounter& InstallWeakCounter(mm::ThreadData& threadData, ObjHeader* objHeader
mm::AllocateObject(&threadData, typeHolderWeakCounter.typeInfo(), location);
auto& weakCounter = WeakCounter::FromObjHeader(*location);
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;
return weakCounter;
}
@@ -222,6 +223,7 @@ public:
~SameThreadMarkAndSweepTest() {
mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests();
mm::GlobalData::Instance().gcScheduler().ReplaceGCSchedulerDataForTests(
[](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();
}
ObjHeader** ObjHeader::GetWeakCounterLocation() {
return &this->meta_object()->WeakReference.counter_;
ObjHeader* ObjHeader::GetWeakCounter() {
return this->meta_object()->WeakReference.counter_;
}
ObjHeader* ObjHeader::GetOrSetWeakCounter(ObjHeader* counter) {
UpdateHeapRefIfNull(&meta_object()->WeakReference.counter_, counter);
return GetWeakCounter();
}
#if KONAN_OBJC_INTEROP
+3 -1
View File
@@ -68,7 +68,9 @@ struct ObjHeader {
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
ALWAYS_INLINE void* GetAssociatedObject();
+8 -4
View File
@@ -68,14 +68,14 @@ OBJ_GETTER(Konan_getWeakReferenceImpl, ObjHeader* referred) {
}
#endif // KONAN_OBJC_INTEROP
ObjHeader** weakCounterLocation = referred->GetWeakCounterLocation();
if (*weakCounterLocation == nullptr) {
ObjHeader* weakCounter = referred->GetWeakCounter();
if (weakCounter == nullptr) {
ObjHolder counterHolder;
// Cast unneeded, just to emphasize we store an object reference as void*.
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.
@@ -89,6 +89,10 @@ OBJ_GETTER(Konan_WeakReferenceCounter_get, ObjHeader* counter) {
#endif
}
ALWAYS_INLINE ObjHeader* UnsafeWeakReferenceCounterGet(ObjHeader* counter) {
return asWeakReferenceCounter(counter)->referred;
}
void WeakReferenceCounterClear(ObjHeader* counter) {
ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred;
// Note, that we don't do UpdateRef here, as reference is weak.
@@ -12,6 +12,7 @@ extern "C" {
// Atomically clears counter object reference.
void WeakReferenceCounterClear(ObjHeader* counter);
ObjHeader* UnsafeWeakReferenceCounterGet(ObjHeader* counter);
} // 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");
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));
if (old != typeInfo) {
@@ -78,18 +78,15 @@ void mm::ExtraObjectData::DetachAssociatedObject() noexcept {
#endif
}
bool mm::ExtraObjectData::HasWeakReferenceCounter() noexcept {
return weakReferenceCounter_ != nullptr;
}
void mm::ExtraObjectData::ClearWeakReferenceCounter() noexcept {
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,
// and so cannot affect marking.
// TODO: Asserts on the above?
weakReferenceCounter_ = nullptr;
weakReferenceCounterOrBaseObject_ = object;
}
mm::ExtraObjectData::~ExtraObjectData() {
@@ -15,6 +15,7 @@
#include "TypeInfo.h"
#include "Utils.hpp"
#include "MultiSourceQueue.hpp"
#include "Weak.h"
namespace kotlin {
namespace mm {
@@ -28,6 +29,8 @@ public:
FLAGS_NEVER_FROZEN = 1 << 1,
};
static constexpr unsigned WEAK_REF_TAG = 1;
MetaObjHeader* AsMetaObjHeader() noexcept { return reinterpret_cast<MetaObjHeader*>(this); }
static ExtraObjectData& FromMetaObjHeader(MetaObjHeader* header) noexcept { return *reinterpret_cast<ExtraObjectData*>(header); }
@@ -45,14 +48,35 @@ public:
#endif
void DetachAssociatedObject() noexcept;
ObjHeader** GetWeakCounterLocation() noexcept { return &weakReferenceCounter_; }
std::atomic<Flags>& flags() noexcept { return flags_; }
bool HasWeakReferenceCounter() noexcept;
bool HasWeakReferenceCounter() noexcept { return hasPointerBits(weakReferenceCounterOrBaseObject_.load(), WEAK_REF_TAG); }
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();
private:
@@ -65,7 +89,7 @@ private:
void* associatedObject_ = nullptr;
#endif
ObjHeader* weakReferenceCounter_ = nullptr;
std::atomic<ObjHeader*> weakReferenceCounterOrBaseObject_;
};
} // namespace mm
@@ -15,12 +15,24 @@ mm::ExtraObjectDataFactory& mm::ExtraObjectDataFactory::Instance() noexcept {
return GlobalData::Instance().extraObjectDataFactory();
}
mm::ExtraObjectData& mm::ExtraObjectDataFactory::CreateExtraObjectDataForObject(mm::ThreadData* threadData, TypeInfo* info) noexcept {
return **threadData->extraObjectDataThreadQueue().Emplace(info);
mm::ExtraObjectData& mm::ExtraObjectDataFactory::CreateExtraObjectDataForObject(
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 {
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 {
@@ -33,9 +33,11 @@ public:
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(ThreadQueue& threadQueue, ExtraObjectData& data) noexcept;
// Collect extra data objects from thread corresponding to `threadData`. Must be called by the thread
// when it's asked by GC to stop.
@@ -23,9 +23,20 @@ struct EmptyPayload {
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
TEST(ExtraObjectDataTest, Install) {
TEST_F(ExtraObjectDataTest, Install) {
ScopedMemoryInit init;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
@@ -38,6 +49,8 @@ TEST(ExtraObjectDataTest, Install) {
EXPECT_TRUE(object.header()->has_meta_object());
EXPECT_THAT(object.header()->meta_object(), extraData.AsMetaObjHeader());
EXPECT_THAT(object.header()->type_info(), typeInfo);
EXPECT_FALSE(extraData.HasWeakReferenceCounter());
EXPECT_THAT(extraData.GetBaseObject(), object.header());
mm::ExtraObjectData::Uninstall(object.header());
@@ -45,7 +58,7 @@ TEST(ExtraObjectDataTest, Install) {
EXPECT_THAT(object.header()->type_info(), typeInfo);
}
TEST(ExtraObjectDataTest, ConcurrentInstall) {
TEST_F(ExtraObjectDataTest, ConcurrentInstall) {
ScopedMemoryInit init;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
@@ -65,13 +78,13 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
}
auto& extraData = mm::ExtraObjectData::Install(object.header());
actual[i] = &extraData;
mm::GlobalData::Instance().threadRegistry().CurrentThreadData()->Publish();
});
}
while (readyCount < kThreadCount) {
}
canStart = true;
for (auto& t : threads) {
t.join();
}
@@ -79,6 +92,4 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
std::vector<mm::ExtraObjectData*> expected(kThreadCount, actual[0]);
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
ObjHeader** ObjHeader::GetWeakCounterLocation() {
return mm::ExtraObjectData::FromMetaObjHeader(this->meta_object()).GetWeakCounterLocation();
ObjHeader* ObjHeader::GetWeakCounter() {
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
@@ -22,37 +22,38 @@ bool isEmpty(T& iterable) {
return iterable.begin() == iterable.end();
}
template <typename E, typename T>
std::vector<E> collect(T& iterable) {
std::vector<E> result;
for (E element : iterable) {
template <typename T>
auto collectCopy(T& iterable) {
std::vector<std::remove_reference_t<decltype(*iterable.begin())>> result;
for (const auto &element : iterable) {
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;
}
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
extern "C" void Kotlin_TestSupport_AssertClearGlobalState() {
// Validate that global registries are empty.
auto globals = mm::GlobalsRegistry::Instance().LockForIter();
auto extraObjects = mm::GlobalData::Instance().extraObjectDataFactory().LockForIter();
auto objects = mm::GlobalData::Instance().objectFactory().LockForIter();
auto stableRefs = mm::StableRefRegistry::Instance().LockForIter();
auto threads = mm::ThreadRegistry::Instance().LockForIter();
EXPECT_THAT(collect<ObjHeader**>(globals), testing::UnorderedElementsAre());
EXPECT_THAT(collect<mm::ObjectFactory<gc::GC>::NodeRef>(objects), testing::UnorderedElementsAre());
EXPECT_THAT(collect<ObjHeader*>(stableRefs), testing::UnorderedElementsAre());
EXPECT_THAT(collect(threads), testing::UnorderedElementsAre());
EXPECT_THAT(collectCopy(globals), testing::UnorderedElementsAre());
EXPECT_THAT(collectPointers(extraObjects), testing::UnorderedElementsAre());
EXPECT_THAT(collectCopy(objects), testing::UnorderedElementsAre());
EXPECT_THAT(collectCopy(stableRefs), testing::UnorderedElementsAre());
EXPECT_THAT(collectPointers(threads), testing::UnorderedElementsAre());
}
void kotlin::DeinitMemoryForTests(MemoryState* memoryState) {