[K/N] Copy stms GC to start cms development
This commit is contained in:
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SameThreadMarkAndSweep.hpp"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include "CompilerConstants.hpp"
|
||||||
|
#include "GlobalData.hpp"
|
||||||
|
#include "Logging.hpp"
|
||||||
|
#include "MarkAndSweepUtils.hpp"
|
||||||
|
#include "Memory.h"
|
||||||
|
#include "RootSet.hpp"
|
||||||
|
#include "Runtime.h"
|
||||||
|
#include "ThreadData.hpp"
|
||||||
|
#include "ThreadRegistry.hpp"
|
||||||
|
#include "ThreadSuspension.hpp"
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct MarkTraits {
|
||||||
|
static bool IsMarked(ObjHeader* object) noexcept {
|
||||||
|
auto& objectData = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(object).GCObjectData();
|
||||||
|
return objectData.color() == gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryMark(ObjHeader* object) noexcept {
|
||||||
|
auto& objectData = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(object).GCObjectData();
|
||||||
|
if (objectData.color() == gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack) return false;
|
||||||
|
objectData.setColor(gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SweepTraits {
|
||||||
|
using ObjectFactory = mm::ObjectFactory<gc::SameThreadMarkAndSweep>;
|
||||||
|
using ExtraObjectsFactory = mm::ExtraObjectDataFactory;
|
||||||
|
|
||||||
|
static bool IsMarkedByExtraObject(mm::ExtraObjectData &object) noexcept {
|
||||||
|
auto *baseObject = object.GetBaseObject();
|
||||||
|
if (!baseObject->heap()) return true;
|
||||||
|
auto& objectData = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(baseObject).GCObjectData();
|
||||||
|
return objectData.color() == gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryResetMark(ObjectFactory::NodeRef node) noexcept {
|
||||||
|
auto& objectData = node.GCObjectData();
|
||||||
|
if (objectData.color() == gc::SameThreadMarkAndSweep::ObjectData::Color::kWhite) return false;
|
||||||
|
objectData.setColor(gc::SameThreadMarkAndSweep::ObjectData::Color::kWhite);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FinalizeTraits {
|
||||||
|
using ObjectFactory = mm::ObjectFactory<gc::SameThreadMarkAndSweep>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global, because it's accessed on a hot path: avoid memory load from `this`.
|
||||||
|
std::atomic<gc::SameThreadMarkAndSweep::SafepointFlag> gSafepointFlag = gc::SameThreadMarkAndSweep::SafepointFlag::kNone;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ALWAYS_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointFunctionPrologue() noexcept {
|
||||||
|
SafePointRegular(GCSchedulerThreadData::kFunctionPrologueWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointLoopBody() noexcept {
|
||||||
|
SafePointRegular(GCSchedulerThreadData::kLoopBodyWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::SameThreadMarkAndSweep::ThreadData::SafePointAllocation(size_t size) noexcept {
|
||||||
|
threadData_.gcScheduler().OnSafePointAllocation(size);
|
||||||
|
SafepointFlag flag = gSafepointFlag.load();
|
||||||
|
if (flag != SafepointFlag::kNone) {
|
||||||
|
SafePointSlowPath(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::SameThreadMarkAndSweep::ThreadData::PerformFullGC() noexcept {
|
||||||
|
auto didGC = gc_.PerformFullGC();
|
||||||
|
|
||||||
|
if (!didGC) {
|
||||||
|
// If we failed to suspend threads, someone else might be asking to suspend them.
|
||||||
|
threadData_.suspensionData().suspendIfRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::SameThreadMarkAndSweep::ThreadData::OnOOM(size_t size) noexcept {
|
||||||
|
RuntimeLogDebug({kTagGC}, "Attempt to GC on OOM at size=%zu", size);
|
||||||
|
PerformFullGC();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointRegular(size_t weight) noexcept {
|
||||||
|
threadData_.gcScheduler().OnSafePointRegular(weight);
|
||||||
|
SafepointFlag flag = gSafepointFlag.load();
|
||||||
|
if (flag != SafepointFlag::kNone) {
|
||||||
|
SafePointSlowPath(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NO_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointSlowPath(SafepointFlag flag) noexcept {
|
||||||
|
RuntimeAssert(flag != SafepointFlag::kNone, "Must've been handled by the caller");
|
||||||
|
// No need to check for kNeedsSuspend, because `suspendIfRequested` checks for its own atomic.
|
||||||
|
threadData_.suspensionData().suspendIfRequested();
|
||||||
|
if (flag == SafepointFlag::kNeedsGC) {
|
||||||
|
RuntimeLogDebug({kTagGC}, "Attempt to GC at SafePoint");
|
||||||
|
PerformFullGC();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gc::SameThreadMarkAndSweep::SameThreadMarkAndSweep() noexcept {
|
||||||
|
mm::GlobalData::Instance().gcScheduler().SetScheduleGC([]() {
|
||||||
|
RuntimeLogDebug({kTagGC}, "Scheduling GC by thread %d", konan::currentThreadId());
|
||||||
|
gSafepointFlag = SafepointFlag::kNeedsGC;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
|
||||||
|
RuntimeLogDebug({kTagGC}, "Attempt to suspend threads by thread %d", konan::currentThreadId());
|
||||||
|
auto timeStartUs = konan::getTimeMicros();
|
||||||
|
bool didSuspend = mm::RequestThreadsSuspension();
|
||||||
|
if (!didSuspend) {
|
||||||
|
RuntimeLogDebug({kTagGC}, "Failed to suspend threads by thread %d", konan::currentThreadId());
|
||||||
|
// Somebody else suspended the threads, and so ran a GC.
|
||||||
|
// TODO: This breaks if suspension is used by something apart from GC.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RuntimeLogDebug({kTagGC}, "Requested thread suspension by thread %d", konan::currentThreadId());
|
||||||
|
gSafepointFlag = SafepointFlag::kNeedsSuspend;
|
||||||
|
|
||||||
|
mm::ObjectFactory<gc::SameThreadMarkAndSweep>::FinalizerQueue finalizerQueue;
|
||||||
|
{
|
||||||
|
// Switch state to native to simulate this thread being a GC thread.
|
||||||
|
ThreadStateGuard guard(ThreadState::kNative);
|
||||||
|
|
||||||
|
mm::WaitForThreadsSuspension();
|
||||||
|
auto timeSuspendUs = konan::getTimeMicros();
|
||||||
|
RuntimeLogDebug({kTagGC}, "Suspended all threads in %" PRIu64 " microseconds", timeSuspendUs - timeStartUs);
|
||||||
|
|
||||||
|
auto& scheduler = mm::GlobalData::Instance().gcScheduler();
|
||||||
|
scheduler.gcData().OnPerformFullGC();
|
||||||
|
|
||||||
|
RuntimeLogInfo(
|
||||||
|
{kTagGC}, "Started GC epoch %zu. Time since last GC %" PRIu64 " microseconds", epoch_, timeStartUs - lastGCTimestampUs_);
|
||||||
|
auto graySet = collectRootSet();
|
||||||
|
auto timeRootSetUs = konan::getTimeMicros();
|
||||||
|
// Can be unsafe, because we've stopped the world.
|
||||||
|
auto objectsCountBefore = mm::GlobalData::Instance().objectFactory().GetSizeUnsafe();
|
||||||
|
|
||||||
|
RuntimeLogInfo(
|
||||||
|
{kTagGC}, "Collected root set of size %zu in %" PRIu64 " microseconds", graySet.size(),
|
||||||
|
timeRootSetUs - timeSuspendUs);
|
||||||
|
auto markStats = gc::Mark<MarkTraits>(std::move(graySet));
|
||||||
|
auto timeMarkUs = konan::getTimeMicros();
|
||||||
|
RuntimeLogDebug({kTagGC}, "Marked %zu objects in %" PRIu64 " microseconds. Processed %zu duplicate entries in the gray set", markStats.aliveHeapSet, timeMarkUs - timeRootSetUs, markStats.duplicateEntries);
|
||||||
|
scheduler.gcData().UpdateAliveSetBytes(markStats.aliveHeapSetBytes);
|
||||||
|
gc::SweepExtraObjects<SweepTraits>(mm::GlobalData::Instance().extraObjectDataFactory());
|
||||||
|
auto timeSweepExtraObjectsUs = konan::getTimeMicros();
|
||||||
|
RuntimeLogDebug({kTagGC}, "Sweeped extra objects in %" PRIu64 " microseconds", timeSweepExtraObjectsUs - timeMarkUs);
|
||||||
|
finalizerQueue = gc::Sweep<SweepTraits>(mm::GlobalData::Instance().objectFactory());
|
||||||
|
auto timeSweepUs = konan::getTimeMicros();
|
||||||
|
RuntimeLogDebug({kTagGC}, "Sweeped in %" PRIu64 " microseconds", timeSweepUs - timeSweepExtraObjectsUs);
|
||||||
|
|
||||||
|
// Can be unsafe, because we've stopped the world.
|
||||||
|
auto objectsCountAfter = mm::GlobalData::Instance().objectFactory().GetSizeUnsafe();
|
||||||
|
auto extraObjectsCountAfter = mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe();
|
||||||
|
|
||||||
|
gSafepointFlag = SafepointFlag::kNone;
|
||||||
|
mm::ResumeThreads();
|
||||||
|
auto timeResumeUs = konan::getTimeMicros();
|
||||||
|
|
||||||
|
RuntimeLogDebug({kTagGC}, "Resumed threads in %" PRIu64 " microseconds.", timeResumeUs - timeSweepUs);
|
||||||
|
|
||||||
|
auto finalizersCount = finalizerQueue.size();
|
||||||
|
auto collectedCount = objectsCountBefore - objectsCountAfter - finalizersCount;
|
||||||
|
|
||||||
|
RuntimeLogInfo(
|
||||||
|
{kTagGC},
|
||||||
|
"Finished GC epoch %zu. Collected %zu objects, to be finalized %zu objects, %zu objects and %zd extra data objects remain. Total pause time %" PRIu64
|
||||||
|
" microseconds",
|
||||||
|
epoch_, collectedCount, finalizersCount, objectsCountAfter, extraObjectsCountAfter, timeResumeUs - timeStartUs);
|
||||||
|
++epoch_;
|
||||||
|
lastGCTimestampUs_ = timeResumeUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalizers are run after threads are resumed, because finalizers may request GC themselves, which would
|
||||||
|
// try to suspend threads again. Also, we run finalizers in the runnable state, because they may be executing
|
||||||
|
// kotlin code.
|
||||||
|
|
||||||
|
// TODO: These will actually need to be run on a separate thread.
|
||||||
|
AssertThreadState(ThreadState::kRunnable);
|
||||||
|
RuntimeLogDebug({kTagGC}, "Starting to run finalizers");
|
||||||
|
auto timeBeforeUs = konan::getTimeMicros();
|
||||||
|
finalizerQueue.Finalize();
|
||||||
|
auto timeAfterUs = konan::getTimeMicros();
|
||||||
|
RuntimeLogInfo({kTagGC}, "Finished running finalizers in %" PRIu64 " microseconds", timeAfterUs - timeBeforeUs);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "Allocator.hpp"
|
||||||
|
#include "GCScheduler.hpp"
|
||||||
|
#include "ObjectFactory.hpp"
|
||||||
|
#include "Types.h"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
namespace kotlin {
|
||||||
|
|
||||||
|
namespace mm {
|
||||||
|
class ThreadData;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace gc {
|
||||||
|
|
||||||
|
// Stop-the-world Mark-and-Sweep that runs on mutator threads. Can support targets that do not have threads.
|
||||||
|
class SameThreadMarkAndSweep : private Pinned {
|
||||||
|
public:
|
||||||
|
enum class SafepointFlag {
|
||||||
|
kNone,
|
||||||
|
kNeedsSuspend,
|
||||||
|
kNeedsGC,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ObjectData {
|
||||||
|
public:
|
||||||
|
enum class Color {
|
||||||
|
kWhite = 0, // Initial color at the start of collection cycles. Objects with this color at the end of GC cycle are collected.
|
||||||
|
// All new objects are allocated with this color.
|
||||||
|
kBlack, // Objects encountered during mark phase.
|
||||||
|
};
|
||||||
|
|
||||||
|
Color color() const noexcept { return color_; }
|
||||||
|
void setColor(Color color) noexcept { color_ = color; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Color color_ = Color::kWhite;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadData : private Pinned {
|
||||||
|
public:
|
||||||
|
using ObjectData = SameThreadMarkAndSweep::ObjectData;
|
||||||
|
using Allocator = AllocatorWithGC<AlignedAllocator, ThreadData>;
|
||||||
|
|
||||||
|
explicit ThreadData(SameThreadMarkAndSweep& gc, mm::ThreadData& threadData) noexcept : gc_(gc), threadData_(threadData) {}
|
||||||
|
~ThreadData() = default;
|
||||||
|
|
||||||
|
void SafePointFunctionPrologue() noexcept;
|
||||||
|
void SafePointLoopBody() noexcept;
|
||||||
|
void SafePointExceptionUnwind() noexcept;
|
||||||
|
void SafePointAllocation(size_t size) noexcept;
|
||||||
|
|
||||||
|
void PerformFullGC() noexcept;
|
||||||
|
|
||||||
|
void OnOOM(size_t size) noexcept;
|
||||||
|
|
||||||
|
Allocator CreateAllocator() noexcept { return Allocator(AlignedAllocator(), *this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SafePointRegular(size_t weight) noexcept;
|
||||||
|
void SafePointSlowPath(SafepointFlag flag) noexcept;
|
||||||
|
|
||||||
|
SameThreadMarkAndSweep& gc_;
|
||||||
|
mm::ThreadData& threadData_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Allocator = ThreadData::Allocator;
|
||||||
|
|
||||||
|
SameThreadMarkAndSweep() noexcept;
|
||||||
|
~SameThreadMarkAndSweep() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Returns `true` if GC has happened, and `false` if not (because someone else has suspended the threads).
|
||||||
|
bool PerformFullGC() noexcept;
|
||||||
|
|
||||||
|
size_t epoch_ = 0;
|
||||||
|
uint64_t lastGCTimestampUs_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gc
|
||||||
|
} // namespace kotlin
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SameThreadMarkAndSweep.hpp"
|
||||||
|
|
||||||
|
namespace kotlin {
|
||||||
|
namespace gc {
|
||||||
|
|
||||||
|
using GC = kotlin::gc::SameThreadMarkAndSweep;
|
||||||
|
|
||||||
|
inline constexpr bool kSupportsMultipleMutators = true;
|
||||||
|
|
||||||
|
} // namespace gc
|
||||||
|
} // namespace kotlin
|
||||||
|
|
||||||
Reference in New Issue
Block a user