GC interface. (#178)

This commit is contained in:
Nikolay Igotti
2017-01-13 12:03:38 +03:00
committed by Konstantin Anisimov
parent ecf66ab23b
commit abd30d2e04
2 changed files with 117 additions and 9 deletions
+71 -7
View File
@@ -27,7 +27,7 @@ ContainerHeader ObjHeader::theStaticObjectsContainer = {
namespace { namespace {
#if USE_GC #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). // release candidates set).
constexpr size_t kGcThreshold = 10000; constexpr size_t kGcThreshold = 10000;
#endif #endif
@@ -41,17 +41,23 @@ typedef std::vector<KRef*> KRefPtrList;
struct MemoryState { struct MemoryState {
// Current number of allocated containers. // Current number of allocated containers.
int allocCount = 0; int allocCount = 0;
#if TRACE_MEMORY #if TRACE_MEMORY
// List of all global objects addresses. // List of all global objects addresses.
KRefPtrList* globalObjects; KRefPtrList* globalObjects;
// Set of all containers. // Set of all containers.
ContainerHeaderSet* containers; ContainerHeaderSet* containers;
#endif #endif
#if USE_GC #if USE_GC
// Set of references to release. // Set of references to release.
ContainerHeaderSet* toFree; ContainerHeaderSet* toFree;
bool gcInProgress; // How many GC suspend requests happened.
int gcSuspendCount;
// How many candidate elements in toFree shall trigger collection.
size_t gcThreshold; size_t gcThreshold;
// If collection is in progress.
bool gcInProgress;
#endif #endif
}; };
@@ -189,7 +195,8 @@ void FreeContainer(ContainerHeader* header) {
#endif #endif
header->ref_count_ = CONTAINER_TAG_INVALID; header->ref_count_ = CONTAINER_TAG_INVALID;
#if USE_GC #if USE_GC
memoryState->toFree->erase(header); if (memoryState->toFree)
memoryState->toFree->erase(header);
#endif #endif
// Now let's clean all object's fields in this container. // 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 // TODO: this is gross hack, relying on the fact that we now only alloc
@@ -227,7 +234,8 @@ void FreeContainerNoRef(ContainerHeader* header) {
#endif #endif
header->ref_count_ = CONTAINER_TAG_INVALID; header->ref_count_ = CONTAINER_TAG_INVALID;
#if USE_GC #if USE_GC
memoryState->toFree->erase(header); if (memoryState->toFree)
memoryState->toFree->erase(header);
#endif #endif
memoryState->allocCount--; memoryState->allocCount--;
free(header); free(header);
@@ -322,9 +330,11 @@ inline void ReleaseRef(const ObjHeader* object) {
#if TRACE_MEMORY #if TRACE_MEMORY
fprintf(stderr, "%p is release candidate\n", object->container()); fprintf(stderr, "%p is release candidate\n", object->container());
#endif #endif
memoryState->toFree->insert(object->container()); if (memoryState->toFree != nullptr) {
if (memoryState->toFree->size() > memoryState->gcThreshold) { memoryState->toFree->insert(object->container());
GarbageCollect(); if (memoryState->gcSuspendCount == 0 &&
memoryState->toFree->size() > memoryState->gcThreshold)
GarbageCollect();
} }
#else // !USE_GC #else // !USE_GC
Release(object->container()); Release(object->container());
@@ -357,6 +367,7 @@ void InitMemory() {
memoryState->toFree = new ContainerHeaderSet(); memoryState->toFree = new ContainerHeaderSet();
memoryState->gcInProgress = false; memoryState->gcInProgress = false;
memoryState->gcThreshold = kGcThreshold; memoryState->gcThreshold = kGcThreshold;
memoryState->gcSuspendCount = 0;
#endif #endif
} }
@@ -560,6 +571,7 @@ void ReleaseGlobalRefs(ObjHeader** start, int count) {
#if USE_GC #if USE_GC
void GarbageCollect() { void GarbageCollect() {
RuntimeAssert(memoryState->toFree != nullptr, "GC must not be stopped");
RuntimeAssert(!memoryState->gcInProgress, "Recursive GC is disallowed"); RuntimeAssert(!memoryState->gcInProgress, "Recursive GC is disallowed");
memoryState->gcInProgress = true; memoryState->gcInProgress = true;
// Traverse inner pointers in the closure of release candidates, and // Traverse inner pointers in the closure of release candidates, and
@@ -626,5 +638,57 @@ void Kotlin_konan_internal_GC_collect(KRef) {
#endif #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" } // extern "C"
+46 -2
View File
@@ -1,8 +1,52 @@
package konan.internal package konan.internal
// Garbage collector interface. // Cycle garbage collector interface.
// TODO: more functions to come. //
// 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 { 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") @SymbolName("Kotlin_konan_internal_GC_collect")
external fun 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)
} }