[K/N] Mark finalizing extra objects to avoid double finalizing

When new mark phase finished before previous finalization is done,
extra objects are still in list, which would be passed to finalize.
This could lead to unpredictable effects.

After this commit such objects are marked by new flag and ignored by
next gc runs.
This commit is contained in:
Pavel Kunyavskiy
2021-12-03 16:31:23 +03:00
committed by Space
parent ebca4d793f
commit 42aaa86f10
3 changed files with 16 additions and 12 deletions
@@ -69,10 +69,11 @@ void SweepExtraObjects(typename Traits::ExtraObjectsFactory& objectFactory) noex
auto iter = objectFactory.LockForIter(); auto iter = objectFactory.LockForIter();
for (auto it = iter.begin(); it != iter.end();) { for (auto it = iter.begin(); it != iter.end();) {
auto &extraObject = *it; auto &extraObject = *it;
if (!Traits::IsMarkedByExtraObject(extraObject)) { if (!extraObject.getFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE) && !Traits::IsMarkedByExtraObject(extraObject)) {
extraObject.ClearWeakReferenceCounter(); extraObject.ClearWeakReferenceCounter();
if (extraObject.HasAssociatedObject()) { if (extraObject.HasAssociatedObject()) {
extraObject.DetachAssociatedObject(); extraObject.DetachAssociatedObject();
extraObject.setFlag(mm::ExtraObjectData::FLAGS_IN_FINALIZER_QUEUE);
++it; ++it;
} else { } else {
extraObject.Uninstall(); extraObject.Uninstall();
@@ -23,10 +23,11 @@ namespace mm {
// Optional data that's lazily allocated only for objects that need it. // Optional data that's lazily allocated only for objects that need it.
class ExtraObjectData : private Pinned, public KonanAllocatorAware { class ExtraObjectData : private Pinned, public KonanAllocatorAware {
public: public:
// flags are stored as single atomic uint32, values are bit numbers in that uint32
enum Flags : uint32_t { enum Flags : uint32_t {
FLAGS_NONE = 0, FLAGS_FROZEN = 0,
FLAGS_FROZEN = 1 << 0, FLAGS_NEVER_FROZEN = 1,
FLAGS_NEVER_FROZEN = 1 << 1, FLAGS_IN_FINALIZER_QUEUE = 2,
}; };
static constexpr unsigned WEAK_REF_TAG = 1; static constexpr unsigned WEAK_REF_TAG = 1;
@@ -49,7 +50,9 @@ public:
bool HasAssociatedObject() noexcept; bool HasAssociatedObject() noexcept;
void DetachAssociatedObject() noexcept; void DetachAssociatedObject() noexcept;
std::atomic<Flags>& flags() noexcept { return flags_; } bool getFlag(Flags value) noexcept { return (flags_.load() & (1u << static_cast<uint32_t>(value))) != 0; }
void setFlag(Flags value) noexcept { flags_.fetch_or(1u << static_cast<uint32_t>(value)); }
bool HasWeakReferenceCounter() noexcept { return hasPointerBits(weakReferenceCounterOrBaseObject_.load(), WEAK_REF_TAG); } bool HasWeakReferenceCounter() noexcept { return hasPointerBits(weakReferenceCounterOrBaseObject_.load(), WEAK_REF_TAG); }
void ClearWeakReferenceCounter() noexcept; void ClearWeakReferenceCounter() noexcept;
@@ -84,7 +87,7 @@ private:
// Must be first to match `TypeInfo` layout. // Must be first to match `TypeInfo` layout.
const TypeInfo* typeInfo_; const TypeInfo* typeInfo_;
std::atomic<Flags> flags_ = FLAGS_NONE; std::atomic<uint32_t> flags_ = 0;
#ifdef KONAN_OBJC_INTEROP #ifdef KONAN_OBJC_INTEROP
void* associatedObject_ = nullptr; void* associatedObject_ = nullptr;
@@ -20,7 +20,7 @@ bool mm::IsFrozen(const ObjHeader* object) noexcept {
} }
if (auto* extraObjectData = mm::ExtraObjectData::Get(object)) { if (auto* extraObjectData = mm::ExtraObjectData::Get(object)) {
return (extraObjectData->flags() & mm::ExtraObjectData::FLAGS_FROZEN) != 0; return extraObjectData->getFlag(mm::ExtraObjectData::FLAGS_FROZEN);
} }
return false; return false;
} }
@@ -48,12 +48,13 @@ ObjHeader* mm::FreezeSubgraph(ObjHeader* root) noexcept {
} }
for (auto* object : objects) { for (auto* object : objects) {
if (auto* extraObjectData = mm::ExtraObjectData::Get(object)) { if (auto* extraObjectData = mm::ExtraObjectData::Get(object)) {
if ((extraObjectData->flags() & mm::ExtraObjectData::FLAGS_NEVER_FROZEN) != 0) return object; if (extraObjectData->getFlag(mm::ExtraObjectData::FLAGS_NEVER_FROZEN)) {
return object;
}
} }
} }
for (auto* object : objects) { for (auto* object : objects) {
auto& flags = mm::ExtraObjectData::GetOrInstall(object).flags(); mm::ExtraObjectData::GetOrInstall(object).setFlag(mm::ExtraObjectData::FLAGS_FROZEN);
flags = static_cast<mm::ExtraObjectData::Flags>(flags | mm::ExtraObjectData::FLAGS_FROZEN);
} }
return nullptr; return nullptr;
} }
@@ -63,7 +64,6 @@ bool mm::EnsureNeverFrozen(ObjHeader* object) noexcept {
return false; return false;
} }
auto& flags = mm::ExtraObjectData::GetOrInstall(object).flags(); mm::ExtraObjectData::GetOrInstall(object).setFlag(mm::ExtraObjectData::FLAGS_NEVER_FROZEN);
flags = static_cast<mm::ExtraObjectData::Flags>(flags | mm::ExtraObjectData::FLAGS_NEVER_FROZEN);
return true; return true;
} }