├── .gitmodules ├── LICENSE ├── Makefile ├── README.Md ├── example.c ├── migc.c └── migc.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mimalloc"] 2 | path = mimalloc 3 | url = https://github.com/microsoft/mimalloc 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Adel Prokurov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CXX=clang++ 3 | CURRENT_DIR=$(shell pwd) 4 | MALLOC_LIBDIR=$(CURRENT_DIR)/mimalloc/out/release 5 | CFLAGS=-O3 6 | LDFLAGS=-L$(TBB_LIBDIR) -Wl,-rpath=$(TBB_LIBDIR) \ 7 | -L$(MALLOC_LIBDIR) -Wl,-rpath=$(MALLOC_LIBDIR) 8 | LIBS=-pthread -lmimalloc 9 | OBJS=migc.o 10 | 11 | libmigc.so: $(OBJS) 12 | $(CC) -shared $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(LIBS) 13 | 14 | $(OBJS): migc.c migc.h Makefile 15 | submodules: 16 | mkdir -p mimalloc/out/release 17 | (cd mimalloc/out/release; cmake ../..) 18 | $(MAKE) -C mimalloc/out/release 19 | 20 | clean: 21 | rm -f *.o *~ migc -------------------------------------------------------------------------------- /README.Md: -------------------------------------------------------------------------------- 1 | # migc 2 | Small and simple library that implements conservative GC using mimalloc API. 3 | 4 | # Features 5 | - Small and tiny. `libmigc.so` is just 20KB when linked with mimalloc. 6 | - Has API that is needed by most users of conservative GCs in C and can replace BDWGC in simple cases (e.g when precise marking is not needed) 7 | There is: `migc_add_roots` and `migc_delete_roots` to add or delete range of pointers to scan for potential pointers, `migc_register_finalizer` to invoke finalizers 8 | on dead objects. 9 | - Fast allocation. When there is multiple instances of migc heap running in multiple threads this library could get much much faster than BDWGC, in single threaded 10 | situations it should be pretty fast too since mimalloc is fast in single threaded apps too. 11 | - Small overhead per allocation: 8 bytes. These 8 bytes is used to store mark,live bit and finalizer pointer (if exists). 12 | # Limitations 13 | - No threading support. migc_heap type can be used only in thread it was created in. 14 | - No API for precise marking, I could add such API in the future but I think it will increase allocation overhead by another 8 bytes. 15 | - Only Mark&Sweep is provided at the moment. There is no advanced GC techniques such as incremental or concurrent marking but sticky generational mark&sweep algorithm could be implemented in the future. 16 | # Building 17 | ```sh 18 | git clone https://github.com/playxe/migc 19 | cd migc 20 | make submodules 21 | make 22 | ``` 23 | This should produce `libmigc.so` which can be dynamically linked with your programs. 24 | 25 | 26 | # Examples 27 | 28 | ```c 29 | #include "migc.h" 30 | // NOTE: current_stack_pointer is provided by migc.h 31 | 32 | int main() { 33 | migc_heap heap; 34 | migc_heap_init(&heap,current_stack_pointer() /*stack start for conservative scanning */,1024/* GC threshold */); 35 | int* mptr = migc_malloc(&heap,sizeof(int)*4); 36 | keep_on_stack(mptr); // <- optional call, this forces compiler to put variable on stack rather than registers so GC can definitely see it 37 | mptr[0] = 42; 38 | printf("%i\n",mptr); 39 | migc_collect(&heap); 40 | } 41 | 42 | 43 | ``` 44 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include "migc.h" 2 | #include 3 | void free1(void *object) 4 | { 5 | printf("Free %p\n", object); 6 | } 7 | void trace1(migc_visitor visitor, void *object) 8 | { 9 | printf("Trace %p\n", object); 10 | migc_visitor_trace(visitor, ((void **)object)[2]); 11 | } 12 | void trace2(migc_visitor visitor, void *object) 13 | { 14 | printf("Trace #2 %p\n", object); 15 | } 16 | 17 | migc_rtti rtti1 = {&free1, &trace1}; 18 | migc_rtti rtti2 = {NULL, &trace2}; 19 | int main() 20 | { 21 | migc_heap heap; 22 | 23 | migc_heap_init(&heap, &heap, 1024); 24 | heap.verbose = 2; 25 | void **obj1 = migc_malloc(&heap, 128, &rtti1); 26 | obj1[2] = migc_malloc(&heap, 16, &rtti2); 27 | migc_collect(&heap); 28 | } -------------------------------------------------------------------------------- /migc.c: -------------------------------------------------------------------------------- 1 | #include "migc.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void migc_heap_init(migc_heap *heap, void *sp, size_t initial_heap_size) 7 | { 8 | heap->sp = sp; 9 | heap->roots = NULL; 10 | heap->allocated = 0; 11 | heap->max_heap_size = initial_heap_size; 12 | heap->mi_heap = mi_heap_new(); 13 | } 14 | 15 | void migc_heap_destroy(migc_heap *heap) 16 | { 17 | mi_heap_destroy(heap->mi_heap); 18 | } 19 | void migc_add_roots(migc_heap *heap, void *from, void *to) 20 | { 21 | conservaive_roots *memory = malloc(sizeof(conservaive_roots)); 22 | 23 | memory->next = NULL; 24 | memory->from = from; 25 | memory->to = to; 26 | memory->next = heap->roots; 27 | heap->roots = memory; 28 | } 29 | 30 | int migc_delete_roots(migc_heap *heap, void *from, void *to) 31 | { 32 | conservaive_roots *prev = heap->roots; 33 | if (prev == NULL) 34 | { 35 | return -1; 36 | } 37 | conservaive_roots *current = prev->next; 38 | while (current != NULL) 39 | { 40 | if (current->from == from && current->to == to) 41 | { 42 | break; 43 | } 44 | prev = current; 45 | current = current->next; 46 | } 47 | if (current == NULL) 48 | { 49 | return 0; 50 | } 51 | prev->next = current->next; 52 | free(current); 53 | return 1; 54 | } 55 | 56 | static void mark_conservative(migc_heap *heap, void *from_, void *to_) 57 | { 58 | void **from = from_; 59 | void **to = to_; 60 | if (heap->verbose) 61 | printf("-mark %p->%p (%zu words)\n", from, to, (size_t)(to - from)); 62 | while (from < to) 63 | { 64 | void *ptr = *from; 65 | if (!ptr) 66 | goto end; 67 | void *real_ptr = ptr - sizeof(mi_gc_header); 68 | 69 | if (mi_heap_check_owned(heap->mi_heap, real_ptr)) 70 | if (mi_heap_contains_block(heap->mi_heap, ptr)) 71 | { 72 | 73 | mi_gc_header *hdr = real_ptr; 74 | if (hdr->live) 75 | { 76 | if (hdr->mark == 0) 77 | { 78 | if (heap->verbose == 2) 79 | printf("--mark %p at %p\n", real_ptr, from); 80 | hdr->mark = 1; 81 | cvector_push_back(heap->mark_stack, real_ptr); 82 | } 83 | } 84 | } 85 | end: 86 | from++; 87 | } 88 | } 89 | 90 | static bool migc_sweep_block(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *arg) 91 | { 92 | if (block == NULL) 93 | { 94 | return 1; 95 | } 96 | migc_heap *theap = arg; 97 | mi_gc_header *hdr = (mi_gc_header *)block; 98 | if (hdr->mark) 99 | { 100 | hdr->mark = 0; 101 | theap->allocated += block_size; 102 | } 103 | else 104 | { 105 | if (hdr->rtti) 106 | { 107 | migc_rtti *rtti = (migc_rtti *)hdr->rtti; 108 | if (rtti->free) 109 | { 110 | finalizer fin = rtti->free; 111 | (fin)(((void *)hdr) + sizeof(mi_gc_header)); 112 | } 113 | } 114 | hdr->mark = 0; 115 | hdr->live = 0; 116 | mi_free(block); 117 | } 118 | return 1; 119 | } 120 | static void __attribute__((noinline)) migc_collect_internal(migc_heap *heap) 121 | { 122 | if (heap->verbose) 123 | printf("GC after %zu bytes allocated (max heap size %zu)\n", heap->allocated, heap->max_heap_size); 124 | jmp_buf buf; 125 | setjmp(&buf); 126 | void *sp = current_stack_pointer(); 127 | heap->current_sp = sp; 128 | void *from = sp; 129 | void *to = heap->sp; 130 | if (from > to) 131 | { 132 | void *tmp = from; 133 | from = to; 134 | to = tmp; 135 | } 136 | heap->mark_stack = NULL; 137 | mark_conservative(heap, from, to); 138 | mark_conservative(heap, &buf, (void **)&buf + (sizeof(jmp_buf) / sizeof(void *) - 1)); 139 | 140 | conservaive_roots *root = heap->roots; 141 | while (root) 142 | { 143 | mark_conservative(heap, root->from, root->to); 144 | root = root->next; 145 | } 146 | migc_visitor visitor; 147 | visitor.heap = heap; 148 | while (cvector_size(heap->mark_stack)) 149 | { 150 | mi_gc_header *hdr = cvector_begin(heap->mark_stack)[cvector_size(heap->mark_stack) - 1]; 151 | cvector_pop_back(heap->mark_stack); 152 | void *from = ((void *)hdr) + 8; 153 | void *to = ((void *)hdr) + mi_usable_size(hdr); 154 | mark_conservative(heap, from, to); 155 | if (hdr->rtti) 156 | { 157 | migc_rtti *rtti = (migc_rtti *)hdr->rtti; 158 | if (rtti->visit) 159 | { 160 | (rtti->visit)(visitor, from); 161 | } 162 | } 163 | } 164 | heap->allocated = 0; 165 | mi_heap_visit_blocks(heap->mi_heap, 1, migc_sweep_block, heap); 166 | 167 | if (heap->allocated > heap->max_heap_size) 168 | { 169 | size_t prev = heap->max_heap_size; 170 | heap->max_heap_size = (size_t)((double)heap->allocated * 1.5); 171 | if (heap->verbose) 172 | printf("Increased GC threshold %zu->%zu", prev, heap->max_heap_size); 173 | } 174 | if (heap->verbose) 175 | { 176 | printf("GC cycle finished with %.3fKB memory in use\n", (double)(heap->allocated) / 1024.0); 177 | } 178 | cvector_free(heap->mark_stack); 179 | } 180 | 181 | void migc_visitor_trace(migc_visitor visitor, void *object) 182 | { 183 | mi_gc_header *hdr = (mi_gc_header *)(object - 8); 184 | if (hdr->mark == 0) 185 | { 186 | hdr->mark = 1; 187 | cvector_push_back(visitor.heap->mark_stack, hdr); 188 | } 189 | } 190 | 191 | void migc_visitor_trace_conservative(migc_visitor visitor, void *from, void *to) 192 | { 193 | mark_conservative(visitor.heap, from, to); 194 | } 195 | 196 | int migc_collect_if_necessary(migc_heap *heap) 197 | { 198 | if (heap->allocated > heap->max_heap_size) 199 | { 200 | 201 | migc_collect_internal(heap); 202 | return 1; 203 | } 204 | return 0; 205 | } 206 | 207 | void migc_collect(migc_heap *heap) 208 | { 209 | migc_collect_internal(heap); 210 | } 211 | void migc_free(migc_heap *heap, void *ptr) 212 | { 213 | mi_gc_header *hdr = ptr - 8; 214 | hdr->live = 0; 215 | if (hdr->rtti) 216 | { 217 | migc_rtti *rtti = (migc_rtti *)hdr->rtti; 218 | if (rtti->free) 219 | { 220 | finalizer fin = rtti->free; 221 | (fin)(ptr); 222 | } 223 | } 224 | mi_free(hdr); 225 | } 226 | 227 | void *migc_realloc(migc_heap *heap, void *pointer, size_t newSize) 228 | { 229 | if (newSize) 230 | { 231 | migc_collect_if_necessary(heap); 232 | size_t real_size = sizeof(mi_gc_header) + mi_good_size(newSize); 233 | void *newHdr = mi_heap_realloc(heap->mi_heap, pointer, real_size); 234 | return newHdr + 8; 235 | } 236 | else 237 | { 238 | migc_free(heap, pointer); 239 | return NULL; 240 | } 241 | } 242 | void *migc_malloc(migc_heap *heap, size_t size, migc_rtti *rtti) 243 | { 244 | migc_collect_if_necessary(heap); 245 | mi_gc_header *hdr; 246 | size_t real_size = mi_good_size(size + sizeof(mi_gc_header)); 247 | if (real_size <= MI_SMALL_SIZE_MAX) 248 | { 249 | hdr = mi_heap_malloc_small(heap->mi_heap, real_size); 250 | } 251 | else 252 | { 253 | hdr = mi_heap_malloc_aligned(heap->mi_heap, real_size, 16); 254 | } 255 | heap->allocated += real_size; 256 | hdr->live = 1; 257 | hdr->mark = 0; 258 | hdr->rtti = (uint64_t)(size_t)rtti; 259 | return ((void *)hdr) + sizeof(mi_gc_header); 260 | } 261 | 262 | void migc_attach_rtti(void *ptr, migc_rtti *rtti) 263 | { 264 | ((mi_gc_header *)(ptr - 8))->rtti = (uint64_t)rtti; 265 | } 266 | 267 | 268 | static volatile void *SINK = 0; 269 | void __keep_on_stack(void *var) 270 | { 271 | SINK = var; 272 | } 273 | -------------------------------------------------------------------------------- /migc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef CVECTOR_H_ 3 | #define CVECTOR_H_ 4 | 5 | #include /* for assert */ 6 | #include /* for malloc/realloc/free */ 7 | 8 | /** 9 | * @brief cvector_vector_type - The vector type used in this library 10 | */ 11 | #define cvector_vector_type(type) type * 12 | 13 | /** 14 | * @brief cvector_set_capacity - For internal use, sets the capacity variable of the vector 15 | * @param vec - the vector 16 | * @param size - the new capacity to set 17 | * @return void 18 | */ 19 | #define cvector_set_capacity(vec, size) \ 20 | do \ 21 | { \ 22 | if (vec) \ 23 | { \ 24 | ((size_t *)(vec))[-1] = (size); \ 25 | } \ 26 | } while (0) 27 | 28 | /** 29 | * @brief cvector_set_size - For internal use, sets the size variable of the vector 30 | * @param vec - the vector 31 | * @param size - the new capacity to set 32 | * @return void 33 | */ 34 | #define cvector_set_size(vec, size) \ 35 | do \ 36 | { \ 37 | if (vec) \ 38 | { \ 39 | ((size_t *)(vec))[-2] = (size); \ 40 | } \ 41 | } while (0) 42 | 43 | /** 44 | * @brief cvector_capacity - gets the current capacity of the vector 45 | * @param vec - the vector 46 | * @return the capacity as a size_t 47 | */ 48 | #define cvector_capacity(vec) \ 49 | ((vec) ? ((size_t *)(vec))[-1] : (size_t)0) 50 | 51 | /** 52 | * @brief cvector_size - gets the current size of the vector 53 | * @param vec - the vector 54 | * @return the size as a size_t 55 | */ 56 | #define cvector_size(vec) \ 57 | ((vec) ? ((size_t *)(vec))[-2] : (size_t)0) 58 | 59 | /** 60 | * @brief cvector_empty - returns non-zero if the vector is empty 61 | * @param vec - the vector 62 | * @return non-zero if empty, zero if non-empty 63 | */ 64 | #define cvector_empty(vec) \ 65 | (cvector_size(vec) == 0) 66 | 67 | /** 68 | * @brief cvector_grow - For internal use, ensures that the vector is at least elements big 69 | * @param vec - the vector 70 | * @param count - the new capacity to set 71 | * @return void 72 | */ 73 | #define cvector_grow(vec, count) \ 74 | do \ 75 | { \ 76 | const size_t cv_sz = (count) * sizeof(*(vec)) + (sizeof(size_t) * 2); \ 77 | if (!(vec)) \ 78 | { \ 79 | size_t *cv_p = malloc(cv_sz); \ 80 | assert(cv_p); \ 81 | (vec) = (void *)(&cv_p[2]); \ 82 | cvector_set_capacity((vec), (count)); \ 83 | cvector_set_size((vec), 0); \ 84 | } \ 85 | else \ 86 | { \ 87 | size_t *cv_p1 = &((size_t *)(vec))[-2]; \ 88 | size_t *cv_p2 = realloc(cv_p1, (cv_sz)); \ 89 | assert(cv_p2); \ 90 | (vec) = (void *)(&cv_p2[2]); \ 91 | cvector_set_capacity((vec), (count)); \ 92 | } \ 93 | } while (0) 94 | 95 | /** 96 | * @brief cvector_pop_back - removes the last element from the vector 97 | * @param vec - the vector 98 | * @return void 99 | */ 100 | #define cvector_pop_back(vec) \ 101 | do \ 102 | { \ 103 | cvector_set_size((vec), cvector_size(vec) - 1); \ 104 | } while (0) 105 | 106 | /** 107 | * @brief cvector_erase - removes the element at index i from the vector 108 | * @param vec - the vector 109 | * @param i - index of element to remove 110 | * @return void 111 | */ 112 | #define cvector_erase(vec, i) \ 113 | do \ 114 | { \ 115 | if (vec) \ 116 | { \ 117 | const size_t cv_sz = cvector_size(vec); \ 118 | if ((i) < cv_sz) \ 119 | { \ 120 | cvector_set_size((vec), cv_sz - 1); \ 121 | size_t cv_x; \ 122 | for (cv_x = (i); cv_x < (cv_sz - 1); ++cv_x) \ 123 | { \ 124 | (vec)[cv_x] = (vec)[cv_x + 1]; \ 125 | } \ 126 | } \ 127 | } \ 128 | } while (0) 129 | 130 | /** 131 | * @brief cvector_free - frees all memory associated with the vector 132 | * @param vec - the vector 133 | * @return void 134 | */ 135 | #define cvector_free(vec) \ 136 | do \ 137 | { \ 138 | if (vec) \ 139 | { \ 140 | size_t *p1 = &((size_t *)(vec))[-2]; \ 141 | free(p1); \ 142 | } \ 143 | } while (0) 144 | 145 | /** 146 | * @brief cvector_begin - returns an iterator to first element of the vector 147 | * @param vec - the vector 148 | * @return a pointer to the first element (or NULL) 149 | */ 150 | #define cvector_begin(vec) \ 151 | (vec) 152 | 153 | /** 154 | * @brief cvector_end - returns an iterator to one past the last element of the vector 155 | * @param vec - the vector 156 | * @return a pointer to one past the last element (or NULL) 157 | */ 158 | #define cvector_end(vec) \ 159 | ((vec) ? &((vec)[cvector_size(vec)]) : NULL) 160 | 161 | /* user request to use logarithmic growth algorithm */ 162 | #ifdef CVECTOR_LOGARITHMIC_GROWTH 163 | 164 | /** 165 | * @brief cvector_push_back - adds an element to the end of the vector 166 | * @param vec - the vector 167 | * @param value - the value to add 168 | * @return void 169 | */ 170 | #define cvector_push_back(vec, value) \ 171 | do \ 172 | { \ 173 | size_t cv_cap = cvector_capacity(vec); \ 174 | if (cv_cap <= cvector_size(vec)) \ 175 | { \ 176 | cvector_grow((vec), !cv_cap ? cv_cap + 1 : cv_cap * 2); \ 177 | } \ 178 | vec[cvector_size(vec)] = (value); \ 179 | cvector_set_size((vec), cvector_size(vec) + 1); \ 180 | } while (0) 181 | 182 | #else 183 | 184 | /** 185 | * @brief cvector_push_back - adds an element to the end of the vector 186 | * @param vec - the vector 187 | * @param value - the value to add 188 | * @return void 189 | */ 190 | #define cvector_push_back(vec, value) \ 191 | do \ 192 | { \ 193 | size_t cv_cap = cvector_capacity(vec); \ 194 | if (cv_cap <= cvector_size(vec)) \ 195 | { \ 196 | cvector_grow((vec), cv_cap + 1); \ 197 | } \ 198 | vec[cvector_size(vec)] = (value); \ 199 | cvector_set_size((vec), cvector_size(vec) + 1); \ 200 | } while (0) 201 | 202 | #endif /* CVECTOR_LOGARITHMIC_GROWTH */ 203 | 204 | /** 205 | * @brief cvector_copy - copy a vector 206 | * @param from - the original vector 207 | * @param to - destination to which the function copy to 208 | * @return void 209 | */ 210 | #define cvector_copy(from, to) \ 211 | do \ 212 | { \ 213 | for (size_t i = 0; i < cvector_size(from); i++) \ 214 | { \ 215 | cvector_push_back(to, from[i]); \ 216 | } \ 217 | } while (0) 218 | 219 | #endif /* CVECTOR_H_ */ 220 | 221 | #include "mimalloc/include/mimalloc.h" 222 | #include 223 | #include 224 | 225 | typedef struct conservative_roots_s 226 | { 227 | struct conservative_roots_s *next; 228 | 229 | void *from; 230 | void *to; 231 | } conservaive_roots; 232 | typedef struct migc_visitor_s migc_visitor; 233 | /// runtime type information that can be used by migc to precisely mark object or invoke finalizer. 234 | typedef struct 235 | { 236 | void (*free)(void *this); 237 | void (*visit)(migc_visitor visitor, void *object); 238 | } migc_rtti; 239 | 240 | typedef struct mi_gc_header_s 241 | { 242 | uint64_t live : 1; 243 | uint64_t mark : 1; 244 | uint64_t rtti : 56; 245 | } mi_gc_header; 246 | 247 | typedef struct migc_heap_s 248 | { 249 | int verbose; 250 | /// Allocated bytes in this GC heap 251 | size_t allocated; 252 | /// Maximum bytes to allocate until GC cycle. This value is dynamically changed after each GC cycle if needed. 253 | size_t max_heap_size; 254 | /// mimalloc heap that is used for allocation inside this GC heap. 255 | mi_heap_t *mi_heap; 256 | 257 | conservaive_roots *roots; 258 | cvector_vector_type(mi_gc_header *) mark_stack; 259 | void *sp; 260 | void *current_sp; 261 | 262 | } migc_heap; 263 | 264 | typedef struct migc_visitor_s 265 | { 266 | migc_heap *heap; 267 | } migc_visitor; 268 | 269 | /// Possibly mark `object`. 270 | /// 271 | /// # Safety 272 | /// 273 | /// `object` must be GC allocated object otherwise this might segfault or lead to UB. 274 | /// 275 | /// 276 | void migc_visitor_trace(migc_visitor visitor, void *object); 277 | /// Mark pointer range conservatively. 278 | void migc_visitor_trace_conservative(migc_visitor visitor, void *from, void *to); 279 | /// Frees `ptr` and invokes finalizer if it exists. 280 | void migc_free(migc_heap *heap, void *ptr); 281 | /// Allocate `size` bytes in GC heap. 282 | void *migc_malloc(migc_heap *heap, size_t size, migc_rtti *rtti); 283 | /// Reallocate `pointer` to `newSize` bytes. 284 | void *migc_realloc(migc_heap *heap, void *pointer, size_t newSize); 285 | /// Initialize MIGC heap. `sp` should point to any on-stack variable for scanning for roots and `initial_heap_size` is GC threshold. 286 | void migc_heap_init(migc_heap *heap, void *sp, size_t initial_heap_size); 287 | /// Destroy MIGC heap. 288 | void migc_heap_destroy(migc_heap *heap); 289 | /// Collect garbage. 290 | void migc_collect(migc_heap *heap); 291 | /// Collect garbage if necessary. 292 | int migc_collect_if_necessary(migc_heap *heap); 293 | /// Add pointer range for scanning for roots. 294 | void migc_add_roots(migc_heap *heap, void *from, void *to); 295 | /// Delete pointer range from potential roots. 296 | int migc_delete_roots(migc_heap *heap, void *from, void *to); 297 | 298 | typedef void (*finalizer)(void *addr); 299 | /// Attach RTTI to `ptr`. If RTTI provides `free` function then it will be invoked when object is deallocated. 300 | /// If `visit` function is provided then this function will be invoked for precise marking of `ptr` internals. 301 | void migc_attach_rtti(void *ptr, migc_rtti *rtti); 302 | 303 | /// Function that can be used to obtain current stack pointer. Internally it does not use any inline assembly but just returns 304 | /// pointer to local variable. 305 | void *__attribute__((noinline)) current_stack_pointer() 306 | { 307 | void *sp = (void *)&sp; 308 | return sp; 309 | } 310 | void __keep_on_stack(void *addr); 311 | #define keep_on_stack(var) __keep_on_stack(&var); --------------------------------------------------------------------------------