mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-26 05:07:41 +00:00 
			
		
		
		
	Garbage collection no longer blows stack.
This commit is contained in:
		| @@ -31,10 +31,11 @@ JANET_THREAD_LOCAL uint32_t janet_vm_gc_interval; | ||||
| JANET_THREAD_LOCAL uint32_t janet_vm_next_collection; | ||||
| JANET_THREAD_LOCAL int janet_vm_gc_suspend = 0; | ||||
|  | ||||
| /* Roots */ | ||||
| JANET_THREAD_LOCAL Janet *janet_vm_roots; | ||||
| JANET_THREAD_LOCAL uint32_t janet_vm_root_count; | ||||
| JANET_THREAD_LOCAL uint32_t janet_vm_root_capacity; | ||||
| /* GC mark state */ | ||||
| JANET_THREAD_LOCAL Janet *janet_vm_gc_marklist = NULL; | ||||
| JANET_THREAD_LOCAL size_t janet_vm_gc_marklist_count = 0; | ||||
| JANET_THREAD_LOCAL size_t janet_vm_gc_marklist_capacity = 0; | ||||
| JANET_THREAD_LOCAL size_t janet_vm_gc_marklist_rootcount = 0; | ||||
|  | ||||
| /* Helpers for marking the various gc types */ | ||||
| static void janet_mark_funcenv(JanetFuncEnv *env); | ||||
| @@ -49,21 +50,17 @@ static void janet_mark_string(const uint8_t *str); | ||||
| static void janet_mark_fiber(JanetFiber *fiber); | ||||
| static void janet_mark_abstract(void *adata); | ||||
|  | ||||
| /* Mark a value */ | ||||
| /* Mark a value (Pushes it to the black list).*/ | ||||
| void janet_mark(Janet x) { | ||||
|     switch (janet_type(x)) { | ||||
|         default: break; | ||||
|         case JANET_STRING: | ||||
|         case JANET_SYMBOL: janet_mark_string(janet_unwrap_string(x)); break; | ||||
|         case JANET_FUNCTION: janet_mark_function(janet_unwrap_function(x)); break; | ||||
|         case JANET_ARRAY: janet_mark_array(janet_unwrap_array(x)); break; | ||||
|         case JANET_TABLE: janet_mark_table(janet_unwrap_table(x)); break; | ||||
|         case JANET_STRUCT: janet_mark_struct(janet_unwrap_struct(x)); break; | ||||
|         case JANET_TUPLE: janet_mark_tuple(janet_unwrap_tuple(x)); break; | ||||
|         case JANET_BUFFER: janet_mark_buffer(janet_unwrap_buffer(x)); break; | ||||
|         case JANET_FIBER: janet_mark_fiber(janet_unwrap_fiber(x)); break; | ||||
|         case JANET_ABSTRACT: janet_mark_abstract(janet_unwrap_abstract(x)); break; | ||||
|     if (janet_vm_gc_marklist_count >= janet_vm_gc_marklist_capacity) { | ||||
|         janet_vm_gc_marklist_capacity = (3 * janet_vm_gc_marklist_count) / 2 + 1; | ||||
|         janet_vm_gc_marklist = realloc(janet_vm_gc_marklist, | ||||
|                 janet_vm_gc_marklist_capacity * sizeof(Janet)); | ||||
|         if (NULL == janet_vm_gc_marklist) { | ||||
|             JANET_OUT_OF_MEMORY; | ||||
|         } | ||||
|     } | ||||
|     janet_vm_gc_marklist[janet_vm_gc_marklist_count++] = x; | ||||
| } | ||||
|  | ||||
| static void janet_mark_string(const uint8_t *str) { | ||||
| @@ -86,10 +83,8 @@ static void janet_mark_abstract(void *adata) { | ||||
| /* Mark a bunch of items in memory */ | ||||
| static void janet_mark_many(const Janet *values, int32_t n) { | ||||
|     const Janet *end = values + n; | ||||
|     while (values < end) { | ||||
|         janet_mark(*values); | ||||
|         values += 1; | ||||
|     } | ||||
|     while (values < end) | ||||
|         janet_mark(*values++); | ||||
| } | ||||
|  | ||||
| /* Mark a bunch of key values items in memory */ | ||||
| @@ -315,10 +310,28 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) { | ||||
|  | ||||
| /* Run garbage collection */ | ||||
| void janet_collect(void) { | ||||
|     uint32_t i; | ||||
|     size_t i; | ||||
|     if (janet_vm_gc_suspend) return; | ||||
|     for (i = 0; i < janet_vm_root_count; i++) | ||||
|         janet_mark(janet_vm_roots[i]); | ||||
|     /* Mark the roots */ | ||||
|     for (i = 0; i < janet_vm_gc_marklist_rootcount; i++) | ||||
|         janet_mark(janet_vm_gc_marklist[i]); | ||||
|     /* While list not empty */ | ||||
|     while (janet_vm_gc_marklist_count > janet_vm_gc_marklist_rootcount) { | ||||
|         Janet x = janet_vm_gc_marklist[--janet_vm_gc_marklist_count]; | ||||
|         switch (janet_type(x)) { | ||||
|             default: break; | ||||
|             case JANET_STRING: | ||||
|             case JANET_SYMBOL: janet_mark_string(janet_unwrap_string(x)); break; | ||||
|             case JANET_FUNCTION: janet_mark_function(janet_unwrap_function(x)); break; | ||||
|             case JANET_ARRAY: janet_mark_array(janet_unwrap_array(x)); break; | ||||
|             case JANET_TABLE: janet_mark_table(janet_unwrap_table(x)); break; | ||||
|             case JANET_STRUCT: janet_mark_struct(janet_unwrap_struct(x)); break; | ||||
|             case JANET_TUPLE: janet_mark_tuple(janet_unwrap_tuple(x)); break; | ||||
|             case JANET_BUFFER: janet_mark_buffer(janet_unwrap_buffer(x)); break; | ||||
|             case JANET_FIBER: janet_mark_fiber(janet_unwrap_fiber(x)); break; | ||||
|             case JANET_ABSTRACT: janet_mark_abstract(janet_unwrap_abstract(x)); break; | ||||
|         } | ||||
|     } | ||||
|     janet_sweep(); | ||||
|     janet_vm_next_collection = 0; | ||||
| } | ||||
| @@ -327,17 +340,8 @@ void janet_collect(void) { | ||||
|  * and all of its children. If gcroot is called on a value n times, unroot | ||||
|  * must also be called n times to remove it as a gc root. */ | ||||
| void janet_gcroot(Janet root) { | ||||
|     uint32_t newcount = janet_vm_root_count + 1; | ||||
|     if (newcount > janet_vm_root_capacity) { | ||||
|         uint32_t newcap = 2 * newcount; | ||||
|         janet_vm_roots = realloc(janet_vm_roots, sizeof(Janet) * newcap); | ||||
|         if (NULL == janet_vm_roots) { | ||||
|             JANET_OUT_OF_MEMORY; | ||||
|         } | ||||
|         janet_vm_root_capacity = newcap; | ||||
|     } | ||||
|     janet_vm_roots[janet_vm_root_count] = root; | ||||
|     janet_vm_root_count = newcount; | ||||
|     janet_mark(root); | ||||
|     janet_vm_gc_marklist_rootcount++; | ||||
| } | ||||
|  | ||||
| /* Identity equality for GC purposes */ | ||||
| @@ -361,12 +365,12 @@ static int janet_gc_idequals(Janet lhs, Janet rhs) { | ||||
| /* Remove a root value from the GC. This allows the gc to potentially reclaim | ||||
|  * a value and all its children. */ | ||||
| int janet_gcunroot(Janet root) { | ||||
|     Janet *vtop = janet_vm_roots + janet_vm_root_count; | ||||
|     Janet *v = janet_vm_roots; | ||||
|     Janet *vtop = janet_vm_gc_marklist + janet_vm_gc_marklist_rootcount; | ||||
|     Janet *v = janet_vm_gc_marklist; | ||||
|     /* Search from top to bottom as access is most likely LIFO */ | ||||
|     for (v = janet_vm_roots; v < vtop; v++) { | ||||
|     for (v = janet_vm_gc_marklist; v < vtop; v++) { | ||||
|         if (janet_gc_idequals(root, *v)) { | ||||
|             *v = janet_vm_roots[--janet_vm_root_count]; | ||||
|             *v = janet_vm_gc_marklist[--janet_vm_gc_marklist_rootcount]; | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
| @@ -375,13 +379,13 @@ int janet_gcunroot(Janet root) { | ||||
|  | ||||
| /* Remove a root value from the GC. This sets the effective reference count to 0. */ | ||||
| int janet_gcunrootall(Janet root) { | ||||
|     Janet *vtop = janet_vm_roots + janet_vm_root_count; | ||||
|     Janet *v = janet_vm_roots; | ||||
|     Janet *vtop = janet_vm_gc_marklist + janet_vm_gc_marklist_rootcount; | ||||
|     Janet *v = janet_vm_gc_marklist; | ||||
|     int ret = 0; | ||||
|     /* Search from top to bottom as access is most likely LIFO */ | ||||
|     for (v = janet_vm_roots; v < vtop; v++) { | ||||
|     for (v = janet_vm_gc_marklist; v < vtop; v++) { | ||||
|         if (janet_gc_idequals(root, *v)) { | ||||
|             *v = janet_vm_roots[--janet_vm_root_count]; | ||||
|             *v = janet_vm_gc_marklist[--janet_vm_gc_marklist_rootcount]; | ||||
|             vtop--; | ||||
|             ret = 1; | ||||
|         } | ||||
|   | ||||
| @@ -55,9 +55,9 @@ extern JANET_THREAD_LOCAL uint32_t janet_vm_gc_interval; | ||||
| extern JANET_THREAD_LOCAL uint32_t janet_vm_next_collection; | ||||
| extern JANET_THREAD_LOCAL int janet_vm_gc_suspend; | ||||
|  | ||||
| /* GC roots */ | ||||
| extern JANET_THREAD_LOCAL Janet *janet_vm_roots; | ||||
| extern JANET_THREAD_LOCAL uint32_t janet_vm_root_count; | ||||
| extern JANET_THREAD_LOCAL uint32_t janet_vm_root_capacity; | ||||
| extern JANET_THREAD_LOCAL Janet *janet_vm_gc_marklist; | ||||
| extern JANET_THREAD_LOCAL size_t janet_vm_gc_marklist_count; | ||||
| extern JANET_THREAD_LOCAL size_t janet_vm_gc_marklist_capacity; | ||||
| extern JANET_THREAD_LOCAL size_t janet_vm_gc_marklist_rootcount; | ||||
|  | ||||
| #endif /* JANET_STATE_H_defined */ | ||||
|   | ||||
| @@ -1335,10 +1335,11 @@ int janet_init(void) { | ||||
|      * there are no memory bugs during development */ | ||||
|     janet_vm_gc_interval = 0x1000000; | ||||
|     janet_symcache_init(); | ||||
|     /* Initialize gc roots */ | ||||
|     janet_vm_roots = NULL; | ||||
|     janet_vm_root_count = 0; | ||||
|     janet_vm_root_capacity = 0; | ||||
|     /* Initialize gc list */ | ||||
|     janet_vm_gc_marklist = NULL; | ||||
|     janet_vm_gc_marklist_count = 0; | ||||
|     janet_vm_gc_marklist_capacity = 0; | ||||
|     janet_vm_gc_marklist_rootcount = 0; | ||||
|     /* Initialize registry */ | ||||
|     janet_vm_registry = janet_table(0); | ||||
|     janet_gcroot(janet_wrap_table(janet_vm_registry)); | ||||
| @@ -1349,9 +1350,7 @@ int janet_init(void) { | ||||
| void janet_deinit(void) { | ||||
|     janet_clear_memory(); | ||||
|     janet_symcache_deinit(); | ||||
|     free(janet_vm_roots); | ||||
|     janet_vm_roots = NULL; | ||||
|     janet_vm_root_count = 0; | ||||
|     janet_vm_root_capacity = 0; | ||||
|     free(janet_vm_gc_marklist); | ||||
|     janet_vm_gc_marklist = NULL; | ||||
|     janet_vm_registry = NULL; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose