GC interface. (#178)
This commit is contained in:
@@ -27,7 +27,7 @@ ContainerHeader ObjHeader::theStaticObjectsContainer = {
|
||||
namespace {
|
||||
|
||||
#if USE_GC
|
||||
// Collection threshold (collect after having so many elements in the
|
||||
// Collection threshold default (collect after having so many elements in the
|
||||
// release candidates set).
|
||||
constexpr size_t kGcThreshold = 10000;
|
||||
#endif
|
||||
@@ -41,17 +41,23 @@ typedef std::vector<KRef*> KRefPtrList;
|
||||
struct MemoryState {
|
||||
// Current number of allocated containers.
|
||||
int allocCount = 0;
|
||||
|
||||
#if TRACE_MEMORY
|
||||
// List of all global objects addresses.
|
||||
KRefPtrList* globalObjects;
|
||||
// Set of all containers.
|
||||
ContainerHeaderSet* containers;
|
||||
#endif
|
||||
|
||||
#if USE_GC
|
||||
// Set of references to release.
|
||||
ContainerHeaderSet* toFree;
|
||||
bool gcInProgress;
|
||||
// How many GC suspend requests happened.
|
||||
int gcSuspendCount;
|
||||
// How many candidate elements in toFree shall trigger collection.
|
||||
size_t gcThreshold;
|
||||
// If collection is in progress.
|
||||
bool gcInProgress;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -189,7 +195,8 @@ void FreeContainer(ContainerHeader* header) {
|
||||
#endif
|
||||
header->ref_count_ = CONTAINER_TAG_INVALID;
|
||||
#if USE_GC
|
||||
memoryState->toFree->erase(header);
|
||||
if (memoryState->toFree)
|
||||
memoryState->toFree->erase(header);
|
||||
#endif
|
||||
// Now let's clean all object's fields in this container.
|
||||
// TODO: this is gross hack, relying on the fact that we now only alloc
|
||||
@@ -227,7 +234,8 @@ void FreeContainerNoRef(ContainerHeader* header) {
|
||||
#endif
|
||||
header->ref_count_ = CONTAINER_TAG_INVALID;
|
||||
#if USE_GC
|
||||
memoryState->toFree->erase(header);
|
||||
if (memoryState->toFree)
|
||||
memoryState->toFree->erase(header);
|
||||
#endif
|
||||
memoryState->allocCount--;
|
||||
free(header);
|
||||
@@ -322,9 +330,11 @@ inline void ReleaseRef(const ObjHeader* object) {
|
||||
#if TRACE_MEMORY
|
||||
fprintf(stderr, "%p is release candidate\n", object->container());
|
||||
#endif
|
||||
memoryState->toFree->insert(object->container());
|
||||
if (memoryState->toFree->size() > memoryState->gcThreshold) {
|
||||
GarbageCollect();
|
||||
if (memoryState->toFree != nullptr) {
|
||||
memoryState->toFree->insert(object->container());
|
||||
if (memoryState->gcSuspendCount == 0 &&
|
||||
memoryState->toFree->size() > memoryState->gcThreshold)
|
||||
GarbageCollect();
|
||||
}
|
||||
#else // !USE_GC
|
||||
Release(object->container());
|
||||
@@ -357,6 +367,7 @@ void InitMemory() {
|
||||
memoryState->toFree = new ContainerHeaderSet();
|
||||
memoryState->gcInProgress = false;
|
||||
memoryState->gcThreshold = kGcThreshold;
|
||||
memoryState->gcSuspendCount = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -560,6 +571,7 @@ void ReleaseGlobalRefs(ObjHeader** start, int count) {
|
||||
|
||||
#if USE_GC
|
||||
void GarbageCollect() {
|
||||
RuntimeAssert(memoryState->toFree != nullptr, "GC must not be stopped");
|
||||
RuntimeAssert(!memoryState->gcInProgress, "Recursive GC is disallowed");
|
||||
memoryState->gcInProgress = true;
|
||||
// Traverse inner pointers in the closure of release candidates, and
|
||||
@@ -626,5 +638,57 @@ void Kotlin_konan_internal_GC_collect(KRef) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_suspend(KRef) {
|
||||
#if USE_GC
|
||||
memoryState->gcSuspendCount++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_resume(KRef) {
|
||||
#if USE_GC
|
||||
if (memoryState->gcSuspendCount > 0) {
|
||||
memoryState->gcSuspendCount--;
|
||||
if (memoryState->toFree != nullptr &&
|
||||
memoryState->toFree->size() >= memoryState->gcThreshold) {
|
||||
GarbageCollect();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_stop(KRef) {
|
||||
#if USE_GC
|
||||
if (memoryState->toFree != nullptr) {
|
||||
GarbageCollect();
|
||||
delete memoryState->toFree;
|
||||
memoryState->toFree = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_start(KRef) {
|
||||
#if USE_GC
|
||||
if (memoryState->toFree == nullptr) {
|
||||
memoryState->toFree = new ContainerHeaderSet();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_setThreshold(KRef, KInt value) {
|
||||
#if USE_GC
|
||||
if (value > 0) {
|
||||
memoryState->gcThreshold = value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KInt Kotlin_konan_internal_GC_getThreshold(KRef) {
|
||||
#if USE_GC
|
||||
return memoryState->gcThreshold;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -1,8 +1,52 @@
|
||||
package konan.internal
|
||||
|
||||
// Garbage collector interface.
|
||||
// TODO: more functions to come.
|
||||
// Cycle garbage collector interface.
|
||||
//
|
||||
// Konan relies upon reference counting for object management, however it could
|
||||
// not collect cyclical garbage, so we perform periodic garbage collection.
|
||||
// This may slow down application, so this interface provides control over how
|
||||
// garbage collector activates and runs.
|
||||
// Garbage collector can be in one of the following states:
|
||||
// * running
|
||||
// * suspended (so cycle candidates are collected, but GC is not performed until resume)
|
||||
// * stopped (all cyclical garbage is hopelessly lost)
|
||||
// Immediately after startup GC is in running state.
|
||||
// Depending on application needs it may select to suspend GC for certain phases of
|
||||
// its lifetime, and resume it later on, or just completely turn it off, if GC pauses
|
||||
// are less desirable than cyclical garbage leaks.
|
||||
object GC {
|
||||
// To force garbage collection immediately, unless collector is stopped
|
||||
// with stop() operation. Even if GC is suspended, collect() still triggers collection.
|
||||
@SymbolName("Kotlin_konan_internal_GC_collect")
|
||||
external fun collect()
|
||||
|
||||
// Suspend garbage collection. Release candidates are still collected, but
|
||||
// GC algorithm is not executed.
|
||||
@SymbolName("Kotlin_konan_internal_GC_suspend")
|
||||
external fun suspend()
|
||||
|
||||
// Resume garbage collection. Can potentially lead to GC immediately.
|
||||
@SymbolName("Kotlin_konan_internal_GC_resume")
|
||||
external fun resume()
|
||||
|
||||
// Stop garbage collection. Cyclical garbage is no longer collected.
|
||||
@SymbolName("Kotlin_konan_internal_GC_stop")
|
||||
external fun stop()
|
||||
|
||||
// Start garbage collection. Cyclical garbage produced while GC was stopped
|
||||
// cannot be reclaimed, but all new garbage is collected.
|
||||
@SymbolName("Kotlin_konan_internal_GC_start")
|
||||
external fun start()
|
||||
|
||||
// GC threshold, controlling how frequenly GC is activated, and how much time GC
|
||||
// takes. Bigger values lead to longer GC pauses, but less GCs.
|
||||
var threshold: Int
|
||||
get() = getThreshold()
|
||||
set(value) = setThreshold(value)
|
||||
|
||||
@SymbolName("Kotlin_konan_internal_GC_getThreshold")
|
||||
private external fun getThreshold(): Int
|
||||
|
||||
@SymbolName("Kotlin_konan_internal_GC_setThreshold")
|
||||
private external fun setThreshold(value: Int)
|
||||
}
|
||||
Reference in New Issue
Block a user