Implemented workers TODO about subrgaph transfer

Implemented check that subgraph of objects can be safely transferred
from one worker to another by running trial deletion on the subgraph.
This commit is contained in:
Igor Chevdar
2017-08-28 14:26:09 +03:00
parent f74265126e
commit 8605c01255
3 changed files with 118 additions and 61 deletions
+103 -58
View File
@@ -19,6 +19,10 @@
#include <cstddef> // for offsetof #include <cstddef> // for offsetof
#ifndef KONAN_NO_THREADS
#include <pthread.h>
#endif
#include "Alloc.h" #include "Alloc.h"
#include "Assert.h" #include "Assert.h"
#include "Exceptions.h" #include "Exceptions.h"
@@ -48,6 +52,14 @@ constexpr container_size_t kObjectAlignment = 8;
#define MEMORY_LOG(...) #define MEMORY_LOG(...)
#endif #endif
inline int atomicAdd(int* where, int what) {
#ifndef KONAN_NO_THREADS
return __sync_add_and_fetch(where, what);
#else
return *where += what;
#endif
}
#if USE_GC #if USE_GC
// Collection threshold default (collect after having so many elements in the // Collection threshold default (collect after having so many elements in the
// release candidates set). Better be a prime number. // release candidates set). Better be a prime number.
@@ -68,9 +80,11 @@ struct FrameOverlay {
ArenaContainer* arena; ArenaContainer* arena;
}; };
// Current number of allocated containers.
int allocCount = 0;
int aliveMemoryStatesCount = 0;
struct MemoryState { struct MemoryState {
// Current number of allocated containers.
int allocCount = 0;
#if TRACE_MEMORY #if TRACE_MEMORY
// List of all global objects addresses. // List of all global objects addresses.
@@ -210,7 +224,7 @@ inline void processFinalizerQueue(MemoryState* state) {
runDeallocationHooks(container); runDeallocationHooks(container);
} }
konanFreeMemory(container); konanFreeMemory(container);
state->allocCount--; atomicAdd(&allocCount, -1);
} }
} }
#endif #endif
@@ -224,7 +238,7 @@ inline void scheduleDestroyContainer(
processFinalizerQueue(state); processFinalizerQueue(state);
} }
#else #else
state->allocCount--; atomicAdd(&allocCount, -1);
konanFreeMemory(header); konanFreeMemory(header);
#endif #endif
} }
@@ -334,9 +348,54 @@ void MarkRoots(MemoryState*);
void DeleteCorpses(MemoryState*); void DeleteCorpses(MemoryState*);
void ScanRoots(MemoryState*); void ScanRoots(MemoryState*);
void CollectRoots(MemoryState*); void CollectRoots(MemoryState*);
void MarkGray(ContainerHeader* container);
template<bool useColor>
void MarkGray(ContainerHeader* container) {
if (useColor) {
if (container->color() == CONTAINER_TAG_GC_GRAY) return;
} else {
if (container->marked()) return;
}
if (useColor) {
container->setColor(CONTAINER_TAG_GC_GRAY);
} else {
container->mark();
}
traverseContainerReferredObjects(container, [](ObjHeader* ref) {
auto childContainer = ref->container();
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
if (!isPermanent(childContainer)) {
childContainer->decRefCount();
MarkGray<useColor>(childContainer);
}
});
}
void Scan(ContainerHeader* container); void Scan(ContainerHeader* container);
void ScanBlack(ContainerHeader* container);
template<bool useColor>
void ScanBlack(ContainerHeader* container) {
if (useColor) {
container->setColor(CONTAINER_TAG_GC_BLACK);
} else {
container->unMark();
}
traverseContainerReferredObjects(container, [](ObjHeader* ref) {
auto childContainer = ref->container();
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
if (!isPermanent(childContainer)) {
childContainer->incRefCount();
if (useColor) {
if (childContainer->color() != CONTAINER_TAG_GC_BLACK)
ScanBlack<useColor>(childContainer);
} else {
if (childContainer->marked())
ScanBlack<useColor>(childContainer);
}
}
});
}
void CollectWhite(MemoryState*, ContainerHeader* container); void CollectWhite(MemoryState*, ContainerHeader* container);
void CollectCycles(MemoryState* state) { void CollectCycles(MemoryState* state) {
@@ -354,7 +413,7 @@ void MarkRoots(MemoryState* state) {
auto color = container->color(); auto color = container->color();
auto rcIsZero = container->refCount() == 0; auto rcIsZero = container->refCount() == 0;
if (color == CONTAINER_TAG_GC_PURPLE && !rcIsZero) { if (color == CONTAINER_TAG_GC_PURPLE && !rcIsZero) {
MarkGray(container); MarkGray<true>(container);
state->roots->push_back(container); state->roots->push_back(container);
} else { } else {
container->resetBuffered(); container->resetBuffered();
@@ -378,23 +437,10 @@ void CollectRoots(MemoryState* state) {
} }
} }
void MarkGray(ContainerHeader* container) {
if (container->color() == CONTAINER_TAG_GC_GRAY) return;
container->setColor(CONTAINER_TAG_GC_GRAY);
traverseContainerReferredObjects(container, [](ObjHeader* ref) {
auto childContainer = ref->container();
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
if (!isPermanent(childContainer)) {
childContainer->decRefCount();
MarkGray(childContainer);
}
});
}
void Scan(ContainerHeader* container) { void Scan(ContainerHeader* container) {
if (container->color() != CONTAINER_TAG_GC_GRAY) return; if (container->color() != CONTAINER_TAG_GC_GRAY) return;
if (container->refCount() != 0) { if (container->refCount() != 0) {
ScanBlack(container); ScanBlack<true>(container);
return; return;
} }
container->setColor(CONTAINER_TAG_GC_WHITE); container->setColor(CONTAINER_TAG_GC_WHITE);
@@ -407,19 +453,6 @@ void Scan(ContainerHeader* container) {
}); });
} }
void ScanBlack(ContainerHeader* container) {
container->setColor(CONTAINER_TAG_GC_BLACK);
traverseContainerReferredObjects(container, [](ObjHeader* ref) {
auto childContainer = ref->container();
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
if (!isPermanent(childContainer)) {
childContainer->incRefCount();
if (childContainer->color() != CONTAINER_TAG_GC_BLACK)
ScanBlack(childContainer);
}
});
}
void CollectWhite(MemoryState* state, ContainerHeader* container) { void CollectWhite(MemoryState* state, ContainerHeader* container) {
if (container->color() != CONTAINER_TAG_GC_WHITE if (container->color() != CONTAINER_TAG_GC_WHITE
|| container->buffered()) || container->buffered())
@@ -495,7 +528,7 @@ ContainerHeader* AllocContainer(size_t size) {
#if TRACE_MEMORY #if TRACE_MEMORY
state->containers->insert(result); state->containers->insert(result);
#endif #endif
state->allocCount++; atomicAdd(&allocCount, 1);
return result; return result;
} }
@@ -678,7 +711,6 @@ MemoryState* InitMemory() {
RuntimeAssert(memoryState == nullptr, "memory state must be clear"); RuntimeAssert(memoryState == nullptr, "memory state must be clear");
memoryState = konanConstructInstance<MemoryState>(); memoryState = konanConstructInstance<MemoryState>();
// TODO: initialize heap here. // TODO: initialize heap here.
memoryState->allocCount = 0;
#if TRACE_MEMORY #if TRACE_MEMORY
memoryState->globalObjects = konanConstructInstance<KRefPtrList>(); memoryState->globalObjects = konanConstructInstance<KRefPtrList>();
memoryState->containers = konanConstructInstance<ContainerHeaderSet>(); memoryState->containers = konanConstructInstance<ContainerHeaderSet>();
@@ -691,6 +723,7 @@ MemoryState* InitMemory() {
initThreshold(memoryState, kGcThreshold); initThreshold(memoryState, kGcThreshold);
memoryState->gcSuspendCount = 0; memoryState->gcSuspendCount = 0;
#endif #endif
atomicAdd(&aliveMemoryStatesCount, 1);
return memoryState; return memoryState;
} }
@@ -716,16 +749,19 @@ void DeinitMemory(MemoryState* memoryState) {
#endif // USE_GC #endif // USE_GC
bool lastMemoryState = atomicAdd(&aliveMemoryStatesCount, -1) == 0;
#if TRACE_MEMORY #if TRACE_MEMORY
if (memoryState->allocCount > 0) { if (allocCount > 0) {
MEMORY_LOG("*** Memory leaks, leaked %d containers ***\n", MEMORY_LOG("*** Memory leaks, leaked %d containers ***\n",
memoryState->allocCount); allocCount);
dumpReachable("", memoryState->containers); dumpReachable("", memoryState->containers);
} }
konanDestructInstance(memoryState->containers); konanDestructInstance(memoryState->containers);
memoryState->containers = nullptr; memoryState->containers = nullptr;
#else #else
RuntimeAssert(memoryState->allocCount == 0, "Memory leaks found"); if (lastMemoryState)
RuntimeAssert(allocCount == 0, "Memory leaks found");
#endif #endif
konanFreeMemory(memoryState); konanFreeMemory(memoryState);
@@ -972,33 +1008,43 @@ OBJ_GETTER(AdoptStablePointer, KNativePtr pointer) {
return ref; return ref;
} }
#if USE_GC
bool hasExternalRefs(ContainerHeader* container, ContainerHeaderSet* visited) {
visited->insert(container);
bool result = container->refCount() != 0;
traverseContainerReferredObjects(container, [&result, visited](ObjHeader* ref) {
auto child = ref->container();
if (!isPermanent(child) && (visited->find(child) == visited->end())) {
result |= hasExternalRefs(child, visited);
}
});
return result;
}
#endif
bool ClearSubgraphReferences(ObjHeader* root, bool checked) { bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
#if USE_GC #if USE_GC
if (root != nullptr) { if (root != nullptr) {
auto state = memoryState; auto state = memoryState;
auto container = root->container(); auto container = root->container();
ContainerHeaderList todo;
ContainerHeaderSet subgraph; ContainerHeaderSet visited;
todo.push_back(container); if (!checked) {
while (todo.size() > 0) { hasExternalRefs(container, &visited);
auto header = todo.back(); } else {
todo.pop_back(); container->decRefCount();
if (subgraph.count(header) != 0) MarkGray<false>(container);
continue; auto bad = hasExternalRefs(container, &visited);
subgraph.insert(header); ScanBlack<false>(container);
MEMORY_LOG("Calling removeFreeable from ClearSubgraphReferences\n"); container->incRefCount();
traverseContainerReferredObjects(header, [&todo](ObjHeader* ref) { if (bad) return false;
auto child = ref->container();
RuntimeAssert(!isArena(child), "A reference to local object is encountered");
if (!isPermanent(child)) {
todo.push_back(child);
}
});
} }
for (auto it = state->toFree->begin(); it != state->toFree->end(); ++it) { for (auto it = state->toFree->begin(); it != state->toFree->end(); ++it) {
auto container = *it; auto container = *it;
if (subgraph.find(container) != subgraph.end()) { if (visited.find(container) != visited.end()) {
container->resetBuffered(); container->resetBuffered();
container->setColor(CONTAINER_TAG_GC_BLACK); container->setColor(CONTAINER_TAG_GC_BLACK);
*it = reinterpret_cast<ContainerHeader*>(reinterpret_cast<uintptr_t>(container) | 1); *it = reinterpret_cast<ContainerHeader*>(reinterpret_cast<uintptr_t>(container) | 1);
@@ -1006,7 +1052,6 @@ bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
} }
} }
#endif // USE_GC #endif // USE_GC
// TODO: perform trial deletion starting from this root, if in checked mode.
return true; return true;
} }
+13 -3
View File
@@ -43,16 +43,17 @@ typedef enum {
// Those bit masks are applied to objectCount_ field. // Those bit masks are applied to objectCount_ field.
// Shift to get actual object count. // Shift to get actual object count.
CONTAINER_TAG_GC_SHIFT = 3, CONTAINER_TAG_GC_SHIFT = 4,
CONTAINER_TAG_GC_INCREMENT = 1 << CONTAINER_TAG_GC_SHIFT, CONTAINER_TAG_GC_INCREMENT = 1 << CONTAINER_TAG_GC_SHIFT,
// Color of a container. // Color of a container.
CONTAINER_TAG_GC_COLOR_MASK = ((CONTAINER_TAG_GC_INCREMENT >> 1) - 1), CONTAINER_TAG_GC_COLOR_MASK = ((CONTAINER_TAG_GC_INCREMENT >> 2) - 1),
// Colors. // Colors.
CONTAINER_TAG_GC_BLACK = 0, CONTAINER_TAG_GC_BLACK = 0,
CONTAINER_TAG_GC_GRAY = 1, CONTAINER_TAG_GC_GRAY = 1,
CONTAINER_TAG_GC_WHITE = 2, CONTAINER_TAG_GC_WHITE = 2,
CONTAINER_TAG_GC_PURPLE = 3, CONTAINER_TAG_GC_PURPLE = 3,
CONTAINER_TAG_GC_BUFFERED = 4 CONTAINER_TAG_GC_MARKED = 4,
CONTAINER_TAG_GC_BUFFERED = 8
} ContainerTag; } ContainerTag;
typedef uint32_t container_offset_t; typedef uint32_t container_offset_t;
@@ -100,6 +101,15 @@ struct ContainerHeader {
inline void resetBuffered() { inline void resetBuffered() {
objectCount_ &= ~CONTAINER_TAG_GC_BUFFERED; objectCount_ &= ~CONTAINER_TAG_GC_BUFFERED;
} }
inline bool marked() const {
return (objectCount_ & CONTAINER_TAG_GC_MARKED) != 0;
}
inline void mark() {
objectCount_ |= CONTAINER_TAG_GC_MARKED;
}
inline void unMark() {
objectCount_ &= ~CONTAINER_TAG_GC_MARKED;
}
}; };
struct ArrayHeader; struct ArrayHeader;
+2
View File
@@ -65,6 +65,8 @@ KNativePtr transfer(KRef object, KInt mode) {
case CHECKED: case CHECKED:
case UNCHECKED: case UNCHECKED:
if (!ClearSubgraphReferences(object, mode == CHECKED)) { if (!ClearSubgraphReferences(object, mode == CHECKED)) {
// Release reference to the object, as it is not being managed by ObjHolder.
UpdateRef(&object, nullptr);
ThrowWorkerInvalidState(); ThrowWorkerInvalidState();
return nullptr; return nullptr;
} }