GC interface. (#178)
This commit is contained in:
committed by
Konstantin Anisimov
parent
ecf66ab23b
commit
abd30d2e04
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user