10 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
62 static struct DHE * _debug_mem[DHE_SIZE];
64 static inline unsigned int _debug_mem_hash(
const void * ptr) {
68 uintptr_t p = (uintptr_t)ptr;
69 return (p >> 4) & (DHE_SIZE-1);
72 static void _debug_mem_set(
const void* ptr,
size_t size) {
73 unsigned int hash = _debug_mem_hash(ptr);
74 _dhe * x = _debug_mem[hash];
76 x = malloc(
sizeof(_dhe));
92 x = malloc(
sizeof(_dhe));
100 static size_t _debug_mem_get(
const void * ptr) {
101 unsigned int hash = _debug_mem_hash(ptr);
102 _dhe * x = _debug_mem[hash];
105 if (x->ptr == ptr)
return x->size;
111 static void _debug_mem_remove(
const void *ptr) {
112 unsigned int hash = _debug_mem_hash(ptr);
113 _dhe * x = _debug_mem[hash];
117 _debug_mem[hash] = x->next;
134 static int _debug_mem_has(
const void *ptr) {
135 unsigned int hash = _debug_mem_hash(ptr);
136 _dhe * x = _debug_mem[hash];
139 if (x->ptr == ptr)
return 1;
147 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
148 _debug_mem_set(ptr, size);
151 vm.bytesAllocated += size;
156 vm.bytesAllocated -= old;
157 vm.bytesAllocated +=
new;
160 #ifndef KRK_NO_STRESS_GC
161 if (
vm.globalFlags & KRK_GLOBAL_ENABLE_STRESS_GC) {
165 if (
vm.bytesAllocated >
vm.nextGC) {
175 out = realloc(ptr,
new);
178 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
180 if (!_debug_mem_has(ptr)) {
181 fprintf(stderr,
"Invalid reallocation of %p from %zu to %zu\n", ptr, old,
new);
185 size_t t = _debug_mem_get(ptr);
187 fprintf(stderr,
"Invalid reallocation of %p from %zu - should be %zu - to %zu\n", ptr, old, t,
new);
191 _debug_mem_remove(ptr);
194 _debug_mem_set(out,
new);
201 static void freeObject(
KrkObj *
object) {
202 switch (object->
type) {
203 case KRK_OBJ_STRING: {
205 FREE_ARRAY(
char, string->
chars, string->
length + 1);
210 case KRK_OBJ_CODEOBJECT: {
215 FREE_ARRAY(
KrkLocalEntry, function->localNames, function->localNameCount);
216 function->localNameCount = 0;
220 case KRK_OBJ_NATIVE: {
224 case KRK_OBJ_CLOSURE: {
231 case KRK_OBJ_UPVALUE: {
235 case KRK_OBJ_CLASS: {
245 case KRK_OBJ_INSTANCE: {
254 case KRK_OBJ_BOUND_METHOD:
257 case KRK_OBJ_TUPLE: {
263 case KRK_OBJ_BYTES: {
278 if (object->
type == KRK_OBJ_INSTANCE) {
281 object->next = other;
289 if (other->
type == KRK_OBJ_CLASS) {
299 void krk_freeMemoryDebugger(
void) {
300 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
301 for (
unsigned int i = 0; i < DHE_SIZE; ++i) {
302 _dhe * x = _debug_mem[i];
303 _debug_mem[i] = NULL;
310 if (
vm.bytesAllocated != 0) {
318 if (object->
flags & KRK_OBJ_FLAGS_IS_MARKED)
return;
319 object->flags |= KRK_OBJ_FLAGS_IS_MARKED;
321 if (
vm.grayCapacity <
vm.grayCount + 1) {
322 vm.grayCapacity = GROW_CAPACITY(
vm.grayCapacity);
323 vm.grayStack = realloc(
vm.grayStack,
sizeof(
KrkObj*) *
vm.grayCapacity);
324 if (!
vm.grayStack) exit(1);
326 vm.grayStack[
vm.grayCount++] = object;
330 if (!IS_OBJECT(value))
return;
335 for (
size_t i = 0; i < array->
count; ++i) {
340 static void blackenObject(
KrkObj *
object) {
341 switch (object->
type) {
342 case KRK_OBJ_CLOSURE: {
353 case KRK_OBJ_CODEOBJECT: {
359 markArray(&function->positionalArgNames);
360 markArray(&function->keywordArgNames);
361 markArray(&function->chunk.constants);
362 for (
size_t i = 0; i <
function->localNameCount; ++i) {
367 case KRK_OBJ_UPVALUE:
370 case KRK_OBJ_CLASS: {
379 case KRK_OBJ_INSTANCE: {
385 case KRK_OBJ_BOUND_METHOD: {
391 case KRK_OBJ_TUPLE: {
393 markArray(&tuple->
values);
403 static void traceReferences(
void) {
404 while (
vm.grayCount > 0) {
405 KrkObj *
object =
vm.grayStack[--
vm.grayCount];
406 blackenObject(
object);
410 static size_t sweep(
void) {
415 if (object->
flags & (KRK_OBJ_FLAGS_IMMORTAL | KRK_OBJ_FLAGS_IS_MARKED)) {
416 object->flags &= ~(KRK_OBJ_FLAGS_IS_MARKED | KRK_OBJ_FLAGS_SECOND_CHANCE);
418 object =
object->
next;
419 }
else if (object->
flags & KRK_OBJ_FLAGS_SECOND_CHANCE) {
420 KrkObj * unreached = object;
421 object =
object->
next;
422 if (previous != NULL) {
423 previous->
next = object;
427 freeObject(unreached);
430 object->flags |= KRK_OBJ_FLAGS_SECOND_CHANCE;
432 object =
object->
next;
439 for (
size_t i = 0; i < table->capacity; ++i) {
446 static void tableRemoveWhite(
KrkTable * table) {
447 for (
size_t i = 0; i < table->capacity; ++i) {
449 if (IS_OBJECT(entry->key) && !((AS_OBJECT(entry->key))->flags & KRK_OBJ_FLAGS_IS_MARKED)) {
456 for (
KrkValue * slot = thread->
stack; slot && slot < thread->stackTop; ++slot) {
471 static void markRoots(
void) {
474 markThreadRoots(thread);
475 thread = thread->
next;
481 if (
vm.specialMethodNames) {
482 for (
int i = 0; i < METHOD__MAX; ++i) {
488 #ifndef KRK_NO_GC_TRACING
489 static int smartSize(
char _out[100],
size_t s) {
490 #if UINTPTR_MAX == 0xFFFFFFFF
492 char * prefix =
"GMK";
495 char * prefix =
"PTGMK";
497 for (; count > 0 && *prefix; count--, prefix++) {
498 size_t base = 1UL << (count * 10);
501 return snprintf(_out, 100,
"%zu.%1zu %ciB", t, (s - t * base) / (base / 10), *prefix);
504 return snprintf(_out, 100,
"%d B", (
int)s);
509 #ifndef KRK_NO_GC_TRACING
510 struct timespec outTime, inTime;
512 if (
vm.globalFlags & KRK_GLOBAL_REPORT_GC_COLLECTS) {
513 clock_gettime(CLOCK_MONOTONIC, &inTime);
516 size_t bytesBefore =
vm.bytesAllocated;
521 tableRemoveWhite(&
vm.strings);
522 size_t out = sweep();
534 if (
vm.bytesAllocated < 0x4000000) {
535 vm.nextGC =
vm.bytesAllocated * 2;
537 vm.nextGC =
vm.bytesAllocated + 0x4000000;
540 #ifndef KRK_NO_GC_TRACING
541 if (
vm.globalFlags & KRK_GLOBAL_REPORT_GC_COLLECTS) {
542 clock_gettime(CLOCK_MONOTONIC, &outTime);
543 struct timespec diff;
544 diff.tv_sec = outTime.tv_sec - inTime.tv_sec;
545 diff.tv_nsec = outTime.tv_nsec - inTime.tv_nsec;
546 if (diff.tv_nsec < 0) { diff.tv_sec--; diff.tv_nsec += 1000000000L; }
548 char smartBefore[100];
549 smartSize(smartBefore, bytesBefore);
550 char smartAfter[100];
551 smartSize(smartAfter,
vm.bytesAllocated);
552 char smartFreed[100];
553 smartSize(smartFreed, bytesBefore -
vm.bytesAllocated);
555 smartSize(smartNext,
vm.nextGC);
557 fprintf(stderr,
"[gc] %lld.%.9lds %s before; %s after; freed %s in %llu objects; next collection at %s\n",
558 (
long long)diff.tv_sec, diff.tv_nsec,
559 smartBefore,smartAfter,smartFreed,(
unsigned long long)out, smartNext);
565 #ifndef KRK_NO_SYSTEM_MODULES
566 KRK_Function(collect) {
567 FUNCTION_TAKES_NONE();
572 KRK_Function(pause) {
573 FUNCTION_TAKES_NONE();
574 vm.globalFlags |= (KRK_GLOBAL_GC_PAUSED);
578 KRK_Function(resume) {
579 FUNCTION_TAKES_NONE();
580 vm.globalFlags &= ~(KRK_GLOBAL_GC_PAUSED);
594 KRK_DOC(gcModule,
"@brief Namespace containing methods for controlling the garbage collector.");
596 KRK_DOC(BIND_FUNC(gcModule,collect),
597 "@brief Triggers one cycle of garbage collection.");
598 KRK_DOC(BIND_FUNC(gcModule,pause),
599 "@brief Disables automatic garbage collection until @ref resume is called.");
600 KRK_DOC(BIND_FUNC(gcModule,resume),
601 "@brief Re-enable automatic garbage collection after it was stopped by @ref pause ");
Exported methods for the source compiler.
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
void krk_markTable(KrkTable *table)
During a GC scan cycle, mark the contents of a table as used.
void krk_freeObjects(void)
Release all objects.
void krk_markObject(KrkObj *object)
During a GC scan cycle, mark an object as used.
void * krk_reallocate(void *ptr, size_t old, size_t new)
Resize an allocated heap object.
void krk_gcTakeBytes(const void *ptr, size_t size)
Assume ownership of size bytes at ptr.
size_t krk_collectGarbage(void)
Run a cycle of the garbage collector.
Struct definitions for core object types.
A function that has been attached to an object to serve as a method.
KrkValue receiver
Object to pass as implicit first argument.
KrkObj * method
Function to call.
Immutable sequence of bytes.
size_t length
Length of data in bytes.
uint8_t * bytes
Pointer to separately-stored bytes data.
void krk_freeChunk(KrkChunk *chunk)
Release the resources allocated to an opcode chunk.
KrkString * filename
Filename of the original source that defined the codeobject for the class.
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
KrkString * name
Name of the class.
struct KrkClass * base
Pointer to base class implementation.
KrkTable subclasses
Set of classes that subclass this class.
size_t allocSize
Size to allocate when creating instances of this class.
KrkTable methods
General attributes table.
struct KrkClass * _class
Metaclass.
KrkCodeObject * function
The codeobject containing the bytecode run when this function is called.
KrkValue globalsOwner
Owner of the globals table for this function.
size_t upvalueCount
Number of entries in upvalues.
KrkValue annotations
Dictionary of type hints.
KrkUpvalue ** upvalues
Array of upvalues collected from the surrounding context when the closure was created.
KrkTable fields
Object attributes table.
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
KrkTable fields
Attributes table.
Metadata on a local variable name in a function.
Managed binding to a C function.
The most basic object type.
uint16_t flags
General object flags, mostly related to garbage collection.
uint16_t type
Tag indicating core type.
struct KrkObj * next
Invasive linked list of all objects in the VM.
Immutable sequence of Unicode codepoints.
char * chars
UTF8 canonical data.
void * codes
Codepoint data.
size_t length
String length in bytes.
One (key,value) pair in a table.
Simple hash table of arbitrary keys to values.
int krk_tableDeleteExact(KrkTable *table, KrkValue key)
Remove a key from a hash table, with identity lookup.
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
void krk_freeTable(KrkTable *table)
Release resources associated with a hash table.
Execution state of a VM thread.
KrkValue currentException
struct KrkThreadState * next
KrkUpvalue * openUpvalues
KrkValue scratchSpace[KRK_THREAD_SCRATCH_SIZE]
Immutable sequence of arbitrary values.
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Storage for values referenced from nested functions.
struct KrkUpvalue * next
Invasive linked list pointer to next upvalue.
Flexible vector of stack references.
void krk_freeValueArray(KrkValueArray *array)
Release relesources used by a value array.
Stack reference or primative value.
Implementation of a generic hash table.
Utilities for creating native bindings.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define KRK_THREAD_SCRATCH_SIZE
Extra space for each thread to store a set of working values safe from the GC.
void krk_module_init_gc(void)
Initialize the built-in 'gc' module.