256 lines
6.5 KiB
C
256 lines
6.5 KiB
C
#include "TracingHeap.h"
|
|
|
|
#include "../pointers/pointers.h"
|
|
|
|
int TracingHeap_Create(TracingHeap* self, allocator_t* allocator)
|
|
{
|
|
if (self == NULL) {
|
|
return EDESTADDRREQ;
|
|
}
|
|
if (1==0
|
|
|| self->config.trace_data == NULL
|
|
|| self->config.destructor == NULL
|
|
|| self->config.size_query == NULL) {
|
|
return EBADSTATE;
|
|
}
|
|
|
|
self->allocator = allocator;
|
|
|
|
self->objects = NULL;
|
|
self->black_objects = NULL;
|
|
self->white_objects = NULL;
|
|
self->grey_objects = NULL;
|
|
|
|
self->reachable_color = TRACINGCOLOR_WHITE;
|
|
self->unreachable_color = TRACINGCOLOR_BLACK;
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static void _TracingHeap_ForceDestroyObject(TracingHeap* self, TracingObject* object)
|
|
{
|
|
self->config.destructor(self->config.destructor_context, object->data);
|
|
size_t allocated_size = self->config.size_query(self->config.size_query_context, object->data);
|
|
Allocator_Free(self->allocator, object, allocated_size);
|
|
}
|
|
|
|
void TracingHeap_Destroy(TracingHeap* self)
|
|
{
|
|
if (self == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (TracingObject* object = self->objects; object != NULL; object = object->global_next) {
|
|
_TracingHeap_ForceDestroyObject(self, object);
|
|
}
|
|
|
|
memset(self, 0, sizeof(*self));
|
|
}
|
|
|
|
static TracingObject* _TracingObject_FromDataPointer(void* datap)
|
|
{
|
|
return rewindp(datap, sizeof(TracingObject));
|
|
}
|
|
|
|
static void _TracingObject_ChangeObjectColor(TracingObject* object, enum TracingColor new_color, TracingObject** list_start)
|
|
{
|
|
if (object->color != new_color) {
|
|
object->color_prev->color_next = object->color_next;
|
|
object->color_next->color_prev = object->color_prev;
|
|
|
|
object->color = new_color;
|
|
object->color_prev = NULL;
|
|
object->color_next = *list_start;
|
|
(*list_start)->color_prev = object;
|
|
*list_start = object;
|
|
}
|
|
}
|
|
|
|
static TracingObject** _TracingHeap_ColorListStart(TracingHeap* self, enum TracingColor color)
|
|
{
|
|
TracingObject** list_start;
|
|
switch (color) {
|
|
case TRACINGCOLOR_BLACK:
|
|
list_start = &self->black_objects;
|
|
break;
|
|
case TRACINGCOLOR_GREY:
|
|
list_start = &self->grey_objects;
|
|
break;
|
|
case TRACINGCOLOR_WHITE:
|
|
list_start = &self->white_objects;
|
|
break;
|
|
}
|
|
|
|
return list_start;
|
|
}
|
|
|
|
static void _TracingHeap_MakeReachable(TracingHeap* self, TracingObject* object)
|
|
{
|
|
TracingObject** reachable_list_start = _TracingHeap_ColorListStart(self, self->reachable_color);
|
|
_TracingObject_ChangeObjectColor(object, self->reachable_color, reachable_list_start);
|
|
}
|
|
|
|
static TracingObject* _TracingHeap_PopColorList(TracingHeap* self, enum TracingColor color)
|
|
{
|
|
TracingObject** list_start = _TracingHeap_ColorListStart(self, color);
|
|
|
|
TracingObject* popped = *list_start;
|
|
popped->color_next->color_prev = NULL; // unlink next element from popped
|
|
*list_start = popped->color_next; // link list to next element
|
|
popped->color_next = NULL; // unlink popped from next element
|
|
|
|
return popped;
|
|
}
|
|
|
|
static int _TracingHeap_TraceReferenceCallback(void* context, void* reference)
|
|
{
|
|
TracingHeap* self = context;
|
|
TracingObject* object = _TracingObject_FromDataPointer(reference);
|
|
|
|
if (object->color == self->unreachable_color) {
|
|
_TracingObject_ChangeObjectColor(object, TRACINGCOLOR_GREY, &self->grey_objects);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int TracingHeap_AddTracingRoot(TracingHeap* self, void* data)
|
|
{
|
|
if (self == NULL) {
|
|
return EDESTADDRREQ;
|
|
}
|
|
|
|
TracingObject* root_object = _TracingObject_FromDataPointer(data);
|
|
_TracingObject_ChangeObjectColor(root_object, TRACINGCOLOR_GREY, &self->grey_objects);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int TracingHeap_TraceNext(TracingHeap* self)
|
|
{
|
|
if (self == NULL) {
|
|
return EDESTADDRREQ;
|
|
}
|
|
|
|
if (self->grey_objects == NULL) {
|
|
return EBADSTATE;
|
|
}
|
|
|
|
TracingObject* trace_object = _TracingHeap_PopColorList(self, TRACINGCOLOR_GREY);
|
|
|
|
int tracing_code = self->config.trace_data(
|
|
self->config.trace_context,
|
|
trace_object->data,
|
|
_TracingHeap_TraceReferenceCallback,
|
|
self
|
|
);
|
|
if (tracing_code) {
|
|
return tracing_code;
|
|
}
|
|
|
|
_TracingHeap_MakeReachable(self, trace_object);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
size_t TracingHeap_TraceNextN(TracingHeap* self, size_t n)
|
|
{
|
|
for (size_t i = 0; i < n; i++) {
|
|
if (TracingHeap_TraceNext(self)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static int _TracingHeap_DestroyObject(TracingHeap* self, TracingObject* unreachable_object)
|
|
{
|
|
int destructor_code = self->config.destructor(self->config.destructor_context, unreachable_object->data);
|
|
if (destructor_code) {
|
|
return destructor_code;
|
|
}
|
|
|
|
size_t allocated_size = self->config.size_query(self->config.size_query_context, unreachable_object->data);
|
|
|
|
Allocator_Free(self->allocator, unreachable_object, allocated_size);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int TracingHeap_BeginTrace(TracingHeap* self)
|
|
{
|
|
if (self == NULL) {
|
|
return EDESTADDRREQ;
|
|
}
|
|
if (self->grey_objects != NULL) {
|
|
return EBADSTATE;
|
|
}
|
|
if (*_TracingHeap_ColorListStart(self, self->unreachable_color) == NULL) {
|
|
return EBADSTATE;
|
|
}
|
|
|
|
// swap reachable and unreachable color
|
|
// xor swap algorithm!!
|
|
self->reachable_color = self->reachable_color ^ self->unreachable_color;
|
|
self->unreachable_color = self->unreachable_color ^ self->reachable_color;
|
|
self->reachable_color = self->reachable_color ^ self->unreachable_color;
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int TracingHeap_EndTrace(TracingHeap* self)
|
|
{
|
|
if (self == NULL) {
|
|
return EDESTADDRREQ;
|
|
}
|
|
if (self->grey_objects != NULL) {
|
|
return EBADSTATE;
|
|
}
|
|
|
|
TracingObject** unreachable_list = _TracingHeap_ColorListStart(self, self->unreachable_color);
|
|
TracingObject* unreachable_object = *unreachable_list;
|
|
|
|
while (unreachable_object != NULL) {
|
|
TracingObject* next_unreachable = unreachable_object->global_next;
|
|
|
|
if (_TracingHeap_DestroyObject(self, unreachable_object)) {
|
|
*unreachable_list = unreachable_object;
|
|
unreachable_object->global_prev = NULL;
|
|
return ECANCELED;
|
|
}
|
|
|
|
unreachable_object = next_unreachable;
|
|
}
|
|
|
|
*unreachable_list = NULL;
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
void* TracingHeap_Allocate(TracingHeap* self, size_t bytes)
|
|
{
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
TracingObject* object = Allocator_Allocate(self->allocator, sizeof(TracingObject) + bytes);
|
|
if (object == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
object->color = self->reachable_color;
|
|
|
|
TracingObject** reachable_list_start = _TracingHeap_ColorListStart(self, self->reachable_color);
|
|
object->color_next = *reachable_list_start;
|
|
*reachable_list_start = object;
|
|
object->color_prev = NULL;
|
|
|
|
|
|
object->global_next = self->objects; // link before global object list
|
|
object->global_prev = NULL; // no previous object
|
|
self->objects->global_prev = object; // link first object via previous link
|
|
self->objects = object; // insert into linked list
|
|
|
|
return object->data;
|
|
}
|