[270] | 1 | /*
|
---|
| 2 | ** gc.c - garbage collector for mruby
|
---|
| 3 | **
|
---|
| 4 | ** See Copyright Notice in mruby.h
|
---|
| 5 | */
|
---|
| 6 |
|
---|
| 7 | #include <string.h>
|
---|
| 8 | #include <stdlib.h>
|
---|
[331] | 9 | #include <mruby.h>
|
---|
| 10 | #include <mruby/array.h>
|
---|
| 11 | #include <mruby/class.h>
|
---|
| 12 | #include <mruby/data.h>
|
---|
[439] | 13 | #include <mruby/istruct.h>
|
---|
[331] | 14 | #include <mruby/hash.h>
|
---|
| 15 | #include <mruby/proc.h>
|
---|
| 16 | #include <mruby/range.h>
|
---|
| 17 | #include <mruby/string.h>
|
---|
| 18 | #include <mruby/variable.h>
|
---|
| 19 | #include <mruby/gc.h>
|
---|
| 20 | #include <mruby/error.h>
|
---|
| 21 | #include <mruby/throw.h>
|
---|
[270] | 22 |
|
---|
| 23 | /*
|
---|
| 24 | = Tri-color Incremental Garbage Collection
|
---|
| 25 |
|
---|
| 26 | mruby's GC is Tri-color Incremental GC with Mark & Sweep.
|
---|
| 27 | Algorithm details are omitted.
|
---|
| 28 | Instead, the implementation part is described below.
|
---|
| 29 |
|
---|
| 30 | == Object's Color
|
---|
| 31 |
|
---|
| 32 | Each object can be painted in three colors:
|
---|
| 33 |
|
---|
| 34 | * White - Unmarked.
|
---|
| 35 | * Gray - Marked, But the child objects are unmarked.
|
---|
| 36 | * Black - Marked, the child objects are also marked.
|
---|
| 37 |
|
---|
| 38 | == Two White Types
|
---|
| 39 |
|
---|
| 40 | There're two white color types in a flip-flop fashion: White-A and White-B,
|
---|
| 41 | which respectively represent the Current White color (the newly allocated
|
---|
| 42 | objects in the current GC cycle) and the Sweep Target White color (the
|
---|
| 43 | dead objects to be swept).
|
---|
| 44 |
|
---|
| 45 | A and B will be switched just at the beginning of the next GC cycle. At
|
---|
| 46 | that time, all the dead objects have been swept, while the newly created
|
---|
| 47 | objects in the current GC cycle which finally remains White are now
|
---|
| 48 | regarded as dead objects. Instead of traversing all the White-A objects and
|
---|
| 49 | painting them as White-B, just switch the meaning of White-A and White-B as
|
---|
| 50 | this will be much cheaper.
|
---|
| 51 |
|
---|
| 52 | As a result, the objects we sweep in the current GC cycle are always
|
---|
| 53 | left from the previous GC cycle. This allows us to sweep objects
|
---|
| 54 | incrementally, without the disturbance of the newly created objects.
|
---|
| 55 |
|
---|
| 56 | == Execution Timing
|
---|
| 57 |
|
---|
| 58 | GC Execution Time and Each step interval are decided by live objects count.
|
---|
| 59 | List of Adjustment API:
|
---|
| 60 |
|
---|
| 61 | * gc_interval_ratio_set
|
---|
| 62 | * gc_step_ratio_set
|
---|
| 63 |
|
---|
| 64 | For details, see the comments for each function.
|
---|
| 65 |
|
---|
| 66 | == Write Barrier
|
---|
| 67 |
|
---|
| 68 | mruby implementer and C extension library writer must insert a write
|
---|
| 69 | barrier when updating a reference from a field of an object.
|
---|
[331] | 70 | When updating a reference from a field of object A to object B,
|
---|
[270] | 71 | two different types of write barrier are available:
|
---|
| 72 |
|
---|
| 73 | * mrb_field_write_barrier - target B object for a mark.
|
---|
| 74 | * mrb_write_barrier - target A object for a mark.
|
---|
| 75 |
|
---|
| 76 | == Generational Mode
|
---|
| 77 |
|
---|
| 78 | mruby's GC offers an Generational Mode while re-using the tri-color GC
|
---|
| 79 | infrastructure. It will treat the Black objects as Old objects after each
|
---|
| 80 | sweep phase, instead of painting them White. The key ideas are still the same
|
---|
| 81 | as traditional generational GC:
|
---|
| 82 |
|
---|
| 83 | * Minor GC - just traverse the Young objects (Gray objects) in the mark
|
---|
| 84 | phase, then only sweep the newly created objects, and leave
|
---|
| 85 | the Old objects live.
|
---|
| 86 |
|
---|
| 87 | * Major GC - same as a full regular GC cycle.
|
---|
| 88 |
|
---|
| 89 | The difference from "traditional" generational GC is, that the major GC
|
---|
| 90 | in mruby is triggered incrementally in a tri-color manner.
|
---|
| 91 |
|
---|
| 92 |
|
---|
| 93 | For details, see the comments for each function.
|
---|
| 94 |
|
---|
| 95 | */
|
---|
| 96 |
|
---|
| 97 | struct free_obj {
|
---|
| 98 | MRB_OBJECT_HEADER;
|
---|
| 99 | struct RBasic *next;
|
---|
| 100 | };
|
---|
| 101 |
|
---|
| 102 | typedef struct {
|
---|
| 103 | union {
|
---|
| 104 | struct free_obj free;
|
---|
| 105 | struct RBasic basic;
|
---|
| 106 | struct RObject object;
|
---|
| 107 | struct RClass klass;
|
---|
| 108 | struct RString string;
|
---|
| 109 | struct RArray array;
|
---|
| 110 | struct RHash hash;
|
---|
| 111 | struct RRange range;
|
---|
| 112 | struct RData data;
|
---|
[439] | 113 | struct RIStruct istruct;
|
---|
[270] | 114 | struct RProc proc;
|
---|
[331] | 115 | struct REnv env;
|
---|
[439] | 116 | struct RFiber fiber;
|
---|
[270] | 117 | struct RException exc;
|
---|
[331] | 118 | struct RBreak brk;
|
---|
[270] | 119 | #ifdef MRB_WORD_BOXING
|
---|
[439] | 120 | #ifndef MRB_WITHOUT_FLOAT
|
---|
[270] | 121 | struct RFloat floatv;
|
---|
[439] | 122 | #endif
|
---|
[270] | 123 | struct RCptr cptr;
|
---|
| 124 | #endif
|
---|
| 125 | } as;
|
---|
| 126 | } RVALUE;
|
---|
| 127 |
|
---|
| 128 | #ifdef GC_PROFILE
|
---|
| 129 | #include <stdio.h>
|
---|
| 130 | #include <sys/time.h>
|
---|
| 131 |
|
---|
| 132 | static double program_invoke_time = 0;
|
---|
| 133 | static double gc_time = 0;
|
---|
| 134 | static double gc_total_time = 0;
|
---|
| 135 |
|
---|
| 136 | static double
|
---|
| 137 | gettimeofday_time(void)
|
---|
| 138 | {
|
---|
| 139 | struct timeval tv;
|
---|
| 140 | gettimeofday(&tv, NULL);
|
---|
| 141 | return tv.tv_sec + tv.tv_usec * 1e-6;
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | #define GC_INVOKE_TIME_REPORT(with) do {\
|
---|
| 145 | fprintf(stderr, "%s\n", with);\
|
---|
| 146 | fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\
|
---|
| 147 | fprintf(stderr, "is_generational: %d\n", is_generational(gc));\
|
---|
[331] | 148 | fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\
|
---|
[270] | 149 | } while(0)
|
---|
| 150 |
|
---|
| 151 | #define GC_TIME_START do {\
|
---|
| 152 | gc_time = gettimeofday_time();\
|
---|
| 153 | } while(0)
|
---|
| 154 |
|
---|
| 155 | #define GC_TIME_STOP_AND_REPORT do {\
|
---|
| 156 | gc_time = gettimeofday_time() - gc_time;\
|
---|
| 157 | gc_total_time += gc_time;\
|
---|
| 158 | fprintf(stderr, "gc_state: %d\n", gc->state);\
|
---|
| 159 | fprintf(stderr, "live: %zu\n", gc->live);\
|
---|
| 160 | fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\
|
---|
| 161 | fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\
|
---|
| 162 | fprintf(stderr, "gc_time: %30.20f\n", gc_time);\
|
---|
| 163 | fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\
|
---|
| 164 | } while(0)
|
---|
| 165 | #else
|
---|
| 166 | #define GC_INVOKE_TIME_REPORT(s)
|
---|
| 167 | #define GC_TIME_START
|
---|
| 168 | #define GC_TIME_STOP_AND_REPORT
|
---|
| 169 | #endif
|
---|
| 170 |
|
---|
| 171 | #ifdef GC_DEBUG
|
---|
| 172 | #define DEBUG(x) (x)
|
---|
| 173 | #else
|
---|
| 174 | #define DEBUG(x)
|
---|
| 175 | #endif
|
---|
| 176 |
|
---|
| 177 | #ifndef MRB_HEAP_PAGE_SIZE
|
---|
| 178 | #define MRB_HEAP_PAGE_SIZE 1024
|
---|
| 179 | #endif
|
---|
| 180 |
|
---|
| 181 | #define GC_STEP_SIZE 1024
|
---|
| 182 |
|
---|
[439] | 183 | /* white: 001 or 010, black: 100, gray: 000 */
|
---|
[270] | 184 | #define GC_GRAY 0
|
---|
| 185 | #define GC_WHITE_A 1
|
---|
| 186 | #define GC_WHITE_B (1 << 1)
|
---|
| 187 | #define GC_BLACK (1 << 2)
|
---|
| 188 | #define GC_WHITES (GC_WHITE_A | GC_WHITE_B)
|
---|
| 189 | #define GC_COLOR_MASK 7
|
---|
| 190 |
|
---|
| 191 | #define paint_gray(o) ((o)->color = GC_GRAY)
|
---|
| 192 | #define paint_black(o) ((o)->color = GC_BLACK)
|
---|
| 193 | #define paint_white(o) ((o)->color = GC_WHITES)
|
---|
| 194 | #define paint_partial_white(s, o) ((o)->color = (s)->current_white_part)
|
---|
| 195 | #define is_gray(o) ((o)->color == GC_GRAY)
|
---|
| 196 | #define is_white(o) ((o)->color & GC_WHITES)
|
---|
| 197 | #define is_black(o) ((o)->color & GC_BLACK)
|
---|
| 198 | #define flip_white_part(s) ((s)->current_white_part = other_white_part(s))
|
---|
| 199 | #define other_white_part(s) ((s)->current_white_part ^ GC_WHITES)
|
---|
| 200 | #define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE)
|
---|
| 201 |
|
---|
| 202 | #define objects(p) ((RVALUE *)p->objects)
|
---|
| 203 |
|
---|
| 204 | MRB_API void*
|
---|
| 205 | mrb_realloc_simple(mrb_state *mrb, void *p, size_t len)
|
---|
| 206 | {
|
---|
| 207 | void *p2;
|
---|
| 208 |
|
---|
| 209 | p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
|
---|
| 210 | if (!p2 && len > 0 && mrb->gc.heaps) {
|
---|
| 211 | mrb_full_gc(mrb);
|
---|
| 212 | p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
|
---|
| 213 | }
|
---|
| 214 |
|
---|
| 215 | return p2;
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 | MRB_API void*
|
---|
| 219 | mrb_realloc(mrb_state *mrb, void *p, size_t len)
|
---|
| 220 | {
|
---|
| 221 | void *p2;
|
---|
| 222 |
|
---|
| 223 | p2 = mrb_realloc_simple(mrb, p, len);
|
---|
[331] | 224 | if (len == 0) return p2;
|
---|
| 225 | if (p2 == NULL) {
|
---|
[270] | 226 | if (mrb->gc.out_of_memory) {
|
---|
[331] | 227 | mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
|
---|
[270] | 228 | /* mrb_panic(mrb); */
|
---|
| 229 | }
|
---|
| 230 | else {
|
---|
| 231 | mrb->gc.out_of_memory = TRUE;
|
---|
| 232 | mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
|
---|
| 233 | }
|
---|
| 234 | }
|
---|
| 235 | else {
|
---|
| 236 | mrb->gc.out_of_memory = FALSE;
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | return p2;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | MRB_API void*
|
---|
| 243 | mrb_malloc(mrb_state *mrb, size_t len)
|
---|
| 244 | {
|
---|
| 245 | return mrb_realloc(mrb, 0, len);
|
---|
| 246 | }
|
---|
| 247 |
|
---|
| 248 | MRB_API void*
|
---|
| 249 | mrb_malloc_simple(mrb_state *mrb, size_t len)
|
---|
| 250 | {
|
---|
| 251 | return mrb_realloc_simple(mrb, 0, len);
|
---|
| 252 | }
|
---|
| 253 |
|
---|
| 254 | MRB_API void*
|
---|
| 255 | mrb_calloc(mrb_state *mrb, size_t nelem, size_t len)
|
---|
| 256 | {
|
---|
| 257 | void *p;
|
---|
| 258 |
|
---|
| 259 | if (nelem > 0 && len > 0 &&
|
---|
| 260 | nelem <= SIZE_MAX / len) {
|
---|
| 261 | size_t size;
|
---|
| 262 | size = nelem * len;
|
---|
| 263 | p = mrb_malloc(mrb, size);
|
---|
| 264 |
|
---|
| 265 | memset(p, 0, size);
|
---|
| 266 | }
|
---|
| 267 | else {
|
---|
| 268 | p = NULL;
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | return p;
|
---|
| 272 | }
|
---|
| 273 |
|
---|
| 274 | MRB_API void
|
---|
| 275 | mrb_free(mrb_state *mrb, void *p)
|
---|
| 276 | {
|
---|
| 277 | (mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
|
---|
| 278 | }
|
---|
| 279 |
|
---|
[439] | 280 | MRB_API void*
|
---|
| 281 | mrb_alloca(mrb_state *mrb, size_t size)
|
---|
| 282 | {
|
---|
| 283 | struct RString *s;
|
---|
| 284 | s = (struct RString*)mrb_obj_alloc(mrb, MRB_TT_STRING, mrb->string_class);
|
---|
| 285 | return s->as.heap.ptr = (char*)mrb_malloc(mrb, size);
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | static mrb_bool
|
---|
| 289 | heap_p(mrb_gc *gc, struct RBasic *object)
|
---|
| 290 | {
|
---|
| 291 | mrb_heap_page* page;
|
---|
| 292 |
|
---|
| 293 | page = gc->heaps;
|
---|
| 294 | while (page) {
|
---|
| 295 | RVALUE *p;
|
---|
| 296 |
|
---|
| 297 | p = objects(page);
|
---|
| 298 | if (&p[0].as.basic <= object && object <= &p[MRB_HEAP_PAGE_SIZE].as.basic) {
|
---|
| 299 | return TRUE;
|
---|
| 300 | }
|
---|
| 301 | page = page->next;
|
---|
| 302 | }
|
---|
| 303 | return FALSE;
|
---|
| 304 | }
|
---|
| 305 |
|
---|
[270] | 306 | MRB_API mrb_bool
|
---|
| 307 | mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) {
|
---|
[439] | 308 | mrb_gc *gc = &mrb->gc;
|
---|
| 309 | if (!heap_p(gc, object)) return TRUE;
|
---|
| 310 | return is_dead(gc, object);
|
---|
[270] | 311 | }
|
---|
| 312 |
|
---|
| 313 | static void
|
---|
| 314 | link_heap_page(mrb_gc *gc, mrb_heap_page *page)
|
---|
| 315 | {
|
---|
| 316 | page->next = gc->heaps;
|
---|
| 317 | if (gc->heaps)
|
---|
| 318 | gc->heaps->prev = page;
|
---|
| 319 | gc->heaps = page;
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | static void
|
---|
| 323 | unlink_heap_page(mrb_gc *gc, mrb_heap_page *page)
|
---|
| 324 | {
|
---|
| 325 | if (page->prev)
|
---|
| 326 | page->prev->next = page->next;
|
---|
| 327 | if (page->next)
|
---|
| 328 | page->next->prev = page->prev;
|
---|
| 329 | if (gc->heaps == page)
|
---|
| 330 | gc->heaps = page->next;
|
---|
| 331 | page->prev = NULL;
|
---|
| 332 | page->next = NULL;
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | static void
|
---|
| 336 | link_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
|
---|
| 337 | {
|
---|
| 338 | page->free_next = gc->free_heaps;
|
---|
| 339 | if (gc->free_heaps) {
|
---|
| 340 | gc->free_heaps->free_prev = page;
|
---|
| 341 | }
|
---|
| 342 | gc->free_heaps = page;
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 | static void
|
---|
| 346 | unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
|
---|
| 347 | {
|
---|
| 348 | if (page->free_prev)
|
---|
| 349 | page->free_prev->free_next = page->free_next;
|
---|
| 350 | if (page->free_next)
|
---|
| 351 | page->free_next->free_prev = page->free_prev;
|
---|
| 352 | if (gc->free_heaps == page)
|
---|
| 353 | gc->free_heaps = page->free_next;
|
---|
| 354 | page->free_prev = NULL;
|
---|
| 355 | page->free_next = NULL;
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 | static void
|
---|
| 359 | add_heap(mrb_state *mrb, mrb_gc *gc)
|
---|
| 360 | {
|
---|
| 361 | mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE));
|
---|
| 362 | RVALUE *p, *e;
|
---|
| 363 | struct RBasic *prev = NULL;
|
---|
| 364 |
|
---|
| 365 | for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
|
---|
| 366 | p->as.free.tt = MRB_TT_FREE;
|
---|
| 367 | p->as.free.next = prev;
|
---|
| 368 | prev = &p->as.basic;
|
---|
| 369 | }
|
---|
| 370 | page->freelist = prev;
|
---|
| 371 |
|
---|
| 372 | link_heap_page(gc, page);
|
---|
| 373 | link_free_heap_page(gc, page);
|
---|
| 374 | }
|
---|
| 375 |
|
---|
| 376 | #define DEFAULT_GC_INTERVAL_RATIO 200
|
---|
| 377 | #define DEFAULT_GC_STEP_RATIO 200
|
---|
[439] | 378 | #define MAJOR_GC_INC_RATIO 120
|
---|
| 379 | #define MAJOR_GC_TOOMANY 10000
|
---|
[270] | 380 | #define is_generational(gc) ((gc)->generational)
|
---|
| 381 | #define is_major_gc(gc) (is_generational(gc) && (gc)->full)
|
---|
| 382 | #define is_minor_gc(gc) (is_generational(gc) && !(gc)->full)
|
---|
| 383 |
|
---|
| 384 | void
|
---|
| 385 | mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
|
---|
| 386 | {
|
---|
| 387 | #ifndef MRB_GC_FIXED_ARENA
|
---|
| 388 | gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE);
|
---|
| 389 | gc->arena_capa = MRB_GC_ARENA_SIZE;
|
---|
| 390 | #endif
|
---|
| 391 |
|
---|
| 392 | gc->current_white_part = GC_WHITE_A;
|
---|
| 393 | gc->heaps = NULL;
|
---|
| 394 | gc->free_heaps = NULL;
|
---|
| 395 | add_heap(mrb, gc);
|
---|
| 396 | gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
|
---|
| 397 | gc->step_ratio = DEFAULT_GC_STEP_RATIO;
|
---|
| 398 | #ifndef MRB_GC_TURN_OFF_GENERATIONAL
|
---|
| 399 | gc->generational = TRUE;
|
---|
| 400 | gc->full = TRUE;
|
---|
| 401 | #endif
|
---|
| 402 |
|
---|
| 403 | #ifdef GC_PROFILE
|
---|
| 404 | program_invoke_time = gettimeofday_time();
|
---|
| 405 | #endif
|
---|
| 406 | }
|
---|
| 407 |
|
---|
[331] | 408 | static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
|
---|
[270] | 409 |
|
---|
[439] | 410 | static void
|
---|
[270] | 411 | free_heap(mrb_state *mrb, mrb_gc *gc)
|
---|
| 412 | {
|
---|
| 413 | mrb_heap_page *page = gc->heaps;
|
---|
| 414 | mrb_heap_page *tmp;
|
---|
| 415 | RVALUE *p, *e;
|
---|
| 416 |
|
---|
| 417 | while (page) {
|
---|
| 418 | tmp = page;
|
---|
| 419 | page = page->next;
|
---|
| 420 | for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
|
---|
| 421 | if (p->as.free.tt != MRB_TT_FREE)
|
---|
[331] | 422 | obj_free(mrb, &p->as.basic, TRUE);
|
---|
[270] | 423 | }
|
---|
| 424 | mrb_free(mrb, tmp);
|
---|
| 425 | }
|
---|
| 426 | }
|
---|
| 427 |
|
---|
| 428 | void
|
---|
| 429 | mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc)
|
---|
| 430 | {
|
---|
| 431 | free_heap(mrb, gc);
|
---|
| 432 | #ifndef MRB_GC_FIXED_ARENA
|
---|
| 433 | mrb_free(mrb, gc->arena);
|
---|
| 434 | #endif
|
---|
| 435 | }
|
---|
| 436 |
|
---|
| 437 | static void
|
---|
| 438 | gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
|
---|
| 439 | {
|
---|
| 440 | #ifdef MRB_GC_FIXED_ARENA
|
---|
| 441 | if (gc->arena_idx >= MRB_GC_ARENA_SIZE) {
|
---|
| 442 | /* arena overflow error */
|
---|
| 443 | gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
|
---|
[331] | 444 | mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err));
|
---|
[270] | 445 | }
|
---|
| 446 | #else
|
---|
| 447 | if (gc->arena_idx >= gc->arena_capa) {
|
---|
| 448 | /* extend arena */
|
---|
[439] | 449 | gc->arena_capa = (int)(gc->arena_capa * 3 / 2);
|
---|
[270] | 450 | gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa);
|
---|
| 451 | }
|
---|
| 452 | #endif
|
---|
| 453 | gc->arena[gc->arena_idx++] = p;
|
---|
| 454 | }
|
---|
| 455 |
|
---|
| 456 | /* mrb_gc_protect() leaves the object in the arena */
|
---|
| 457 | MRB_API void
|
---|
| 458 | mrb_gc_protect(mrb_state *mrb, mrb_value obj)
|
---|
| 459 | {
|
---|
| 460 | if (mrb_immediate_p(obj)) return;
|
---|
| 461 | gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj));
|
---|
| 462 | }
|
---|
| 463 |
|
---|
| 464 | #define GC_ROOT_NAME "_gc_root_"
|
---|
| 465 |
|
---|
| 466 | /* mrb_gc_register() keeps the object from GC.
|
---|
| 467 |
|
---|
| 468 | Register your object when it's exported to C world,
|
---|
| 469 | without reference from Ruby world, e.g. callback
|
---|
[331] | 470 | arguments. Don't forget to remove the object using
|
---|
[270] | 471 | mrb_gc_unregister, otherwise your object will leak.
|
---|
| 472 | */
|
---|
| 473 |
|
---|
| 474 | MRB_API void
|
---|
| 475 | mrb_gc_register(mrb_state *mrb, mrb_value obj)
|
---|
| 476 | {
|
---|
[439] | 477 | mrb_sym root;
|
---|
| 478 | mrb_value table;
|
---|
[270] | 479 |
|
---|
[439] | 480 | if (mrb_immediate_p(obj)) return;
|
---|
| 481 | root = mrb_intern_lit(mrb, GC_ROOT_NAME);
|
---|
| 482 | table = mrb_gv_get(mrb, root);
|
---|
| 483 | if (mrb_nil_p(table) || !mrb_array_p(table)) {
|
---|
[270] | 484 | table = mrb_ary_new(mrb);
|
---|
| 485 | mrb_gv_set(mrb, root, table);
|
---|
| 486 | }
|
---|
| 487 | mrb_ary_push(mrb, table, obj);
|
---|
| 488 | }
|
---|
| 489 |
|
---|
| 490 | /* mrb_gc_unregister() removes the object from GC root. */
|
---|
| 491 | MRB_API void
|
---|
| 492 | mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
|
---|
| 493 | {
|
---|
[439] | 494 | mrb_sym root;
|
---|
| 495 | mrb_value table;
|
---|
[270] | 496 | struct RArray *a;
|
---|
[331] | 497 | mrb_int i;
|
---|
[270] | 498 |
|
---|
[439] | 499 | if (mrb_immediate_p(obj)) return;
|
---|
| 500 | root = mrb_intern_lit(mrb, GC_ROOT_NAME);
|
---|
| 501 | table = mrb_gv_get(mrb, root);
|
---|
[270] | 502 | if (mrb_nil_p(table)) return;
|
---|
[439] | 503 | if (!mrb_array_p(table)) {
|
---|
[270] | 504 | mrb_gv_set(mrb, root, mrb_nil_value());
|
---|
| 505 | return;
|
---|
| 506 | }
|
---|
| 507 | a = mrb_ary_ptr(table);
|
---|
| 508 | mrb_ary_modify(mrb, a);
|
---|
[439] | 509 | for (i = 0; i < ARY_LEN(a); i++) {
|
---|
| 510 | if (mrb_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) {
|
---|
| 511 | mrb_int len = ARY_LEN(a)-1;
|
---|
| 512 | mrb_value *ptr = ARY_PTR(a);
|
---|
| 513 |
|
---|
| 514 | ARY_SET_LEN(a, len);
|
---|
| 515 | memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value));
|
---|
[331] | 516 | break;
|
---|
[270] | 517 | }
|
---|
| 518 | }
|
---|
| 519 | }
|
---|
| 520 |
|
---|
| 521 | MRB_API struct RBasic*
|
---|
| 522 | mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
|
---|
| 523 | {
|
---|
| 524 | struct RBasic *p;
|
---|
[439] | 525 | static const RVALUE RVALUE_zero = { { { NULL, NULL, MRB_TT_FALSE } } };
|
---|
[270] | 526 | mrb_gc *gc = &mrb->gc;
|
---|
| 527 |
|
---|
[331] | 528 | if (cls) {
|
---|
| 529 | enum mrb_vtype tt;
|
---|
| 530 |
|
---|
| 531 | switch (cls->tt) {
|
---|
| 532 | case MRB_TT_CLASS:
|
---|
| 533 | case MRB_TT_SCLASS:
|
---|
| 534 | case MRB_TT_MODULE:
|
---|
| 535 | case MRB_TT_ENV:
|
---|
| 536 | break;
|
---|
| 537 | default:
|
---|
| 538 | mrb_raise(mrb, E_TYPE_ERROR, "allocation failure");
|
---|
| 539 | }
|
---|
| 540 | tt = MRB_INSTANCE_TT(cls);
|
---|
| 541 | if (tt != MRB_TT_FALSE &&
|
---|
| 542 | ttype != MRB_TT_SCLASS &&
|
---|
| 543 | ttype != MRB_TT_ICLASS &&
|
---|
| 544 | ttype != MRB_TT_ENV &&
|
---|
| 545 | ttype != tt) {
|
---|
[439] | 546 | mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls);
|
---|
[331] | 547 | }
|
---|
| 548 | }
|
---|
| 549 |
|
---|
[270] | 550 | #ifdef MRB_GC_STRESS
|
---|
| 551 | mrb_full_gc(mrb);
|
---|
| 552 | #endif
|
---|
| 553 | if (gc->threshold < gc->live) {
|
---|
| 554 | mrb_incremental_gc(mrb);
|
---|
| 555 | }
|
---|
| 556 | if (gc->free_heaps == NULL) {
|
---|
| 557 | add_heap(mrb, gc);
|
---|
| 558 | }
|
---|
| 559 |
|
---|
| 560 | p = gc->free_heaps->freelist;
|
---|
| 561 | gc->free_heaps->freelist = ((struct free_obj*)p)->next;
|
---|
| 562 | if (gc->free_heaps->freelist == NULL) {
|
---|
| 563 | unlink_free_heap_page(gc, gc->free_heaps);
|
---|
| 564 | }
|
---|
| 565 |
|
---|
| 566 | gc->live++;
|
---|
| 567 | gc_protect(mrb, gc, p);
|
---|
| 568 | *(RVALUE *)p = RVALUE_zero;
|
---|
| 569 | p->tt = ttype;
|
---|
| 570 | p->c = cls;
|
---|
| 571 | paint_partial_white(gc, p);
|
---|
| 572 | return p;
|
---|
| 573 | }
|
---|
| 574 |
|
---|
| 575 | static inline void
|
---|
| 576 | add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
|
---|
| 577 | {
|
---|
| 578 | #ifdef MRB_GC_STRESS
|
---|
| 579 | if (obj->tt > MRB_TT_MAXDEFINE) {
|
---|
| 580 | abort();
|
---|
| 581 | }
|
---|
| 582 | #endif
|
---|
| 583 | paint_gray(obj);
|
---|
| 584 | obj->gcnext = gc->gray_list;
|
---|
| 585 | gc->gray_list = obj;
|
---|
| 586 | }
|
---|
| 587 |
|
---|
[439] | 588 | static int
|
---|
| 589 | ci_nregs(mrb_callinfo *ci)
|
---|
| 590 | {
|
---|
| 591 | struct RProc *p = ci->proc;
|
---|
| 592 | int n = 0;
|
---|
| 593 |
|
---|
| 594 | if (!p) {
|
---|
| 595 | if (ci->argc < 0) return 3;
|
---|
| 596 | return ci->argc+2;
|
---|
| 597 | }
|
---|
| 598 | if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
|
---|
| 599 | n = p->body.irep->nregs;
|
---|
| 600 | }
|
---|
| 601 | if (ci->argc < 0) {
|
---|
| 602 | if (n < 3) n = 3; /* self + args + blk */
|
---|
| 603 | }
|
---|
| 604 | if (ci->argc > n) {
|
---|
| 605 | n = ci->argc + 2; /* self + blk */
|
---|
| 606 | }
|
---|
| 607 | return n;
|
---|
| 608 | }
|
---|
| 609 |
|
---|
[270] | 610 | static void
|
---|
| 611 | mark_context_stack(mrb_state *mrb, struct mrb_context *c)
|
---|
| 612 | {
|
---|
| 613 | size_t i;
|
---|
| 614 | size_t e;
|
---|
[331] | 615 | mrb_value nil;
|
---|
[270] | 616 |
|
---|
[331] | 617 | if (c->stack == NULL) return;
|
---|
[270] | 618 | e = c->stack - c->stbase;
|
---|
[331] | 619 | if (c->ci) {
|
---|
[439] | 620 | e += ci_nregs(c->ci);
|
---|
[331] | 621 | }
|
---|
[270] | 622 | if (c->stbase + e > c->stend) e = c->stend - c->stbase;
|
---|
| 623 | for (i=0; i<e; i++) {
|
---|
| 624 | mrb_value v = c->stbase[i];
|
---|
| 625 |
|
---|
| 626 | if (!mrb_immediate_p(v)) {
|
---|
[331] | 627 | mrb_gc_mark(mrb, mrb_basic_ptr(v));
|
---|
[270] | 628 | }
|
---|
| 629 | }
|
---|
[331] | 630 | e = c->stend - c->stbase;
|
---|
| 631 | nil = mrb_nil_value();
|
---|
| 632 | for (; i<e; i++) {
|
---|
| 633 | c->stbase[i] = nil;
|
---|
| 634 | }
|
---|
[270] | 635 | }
|
---|
| 636 |
|
---|
| 637 | static void
|
---|
| 638 | mark_context(mrb_state *mrb, struct mrb_context *c)
|
---|
| 639 | {
|
---|
[331] | 640 | int i;
|
---|
[270] | 641 | mrb_callinfo *ci;
|
---|
| 642 |
|
---|
[439] | 643 | start:
|
---|
| 644 | if (c->status == MRB_FIBER_TERMINATED) return;
|
---|
| 645 |
|
---|
[331] | 646 | /* mark VM stack */
|
---|
[270] | 647 | mark_context_stack(mrb, c);
|
---|
| 648 |
|
---|
[331] | 649 | /* mark call stack */
|
---|
[270] | 650 | if (c->cibase) {
|
---|
| 651 | for (ci = c->cibase; ci <= c->ci; ci++) {
|
---|
| 652 | mrb_gc_mark(mrb, (struct RBasic*)ci->env);
|
---|
| 653 | mrb_gc_mark(mrb, (struct RBasic*)ci->proc);
|
---|
| 654 | mrb_gc_mark(mrb, (struct RBasic*)ci->target_class);
|
---|
| 655 | }
|
---|
| 656 | }
|
---|
| 657 | /* mark ensure stack */
|
---|
[439] | 658 | for (i=0; i<c->eidx; i++) {
|
---|
[270] | 659 | mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]);
|
---|
| 660 | }
|
---|
| 661 | /* mark fibers */
|
---|
[331] | 662 | mrb_gc_mark(mrb, (struct RBasic*)c->fib);
|
---|
| 663 | if (c->prev) {
|
---|
[439] | 664 | c = c->prev;
|
---|
| 665 | goto start;
|
---|
[270] | 666 | }
|
---|
| 667 | }
|
---|
| 668 |
|
---|
| 669 | static void
|
---|
| 670 | gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
|
---|
| 671 | {
|
---|
| 672 | mrb_assert(is_gray(obj));
|
---|
| 673 | paint_black(obj);
|
---|
| 674 | gc->gray_list = obj->gcnext;
|
---|
| 675 | mrb_gc_mark(mrb, (struct RBasic*)obj->c);
|
---|
| 676 | switch (obj->tt) {
|
---|
| 677 | case MRB_TT_ICLASS:
|
---|
| 678 | {
|
---|
| 679 | struct RClass *c = (struct RClass*)obj;
|
---|
[439] | 680 | if (MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_ORIGIN))
|
---|
[270] | 681 | mrb_gc_mark_mt(mrb, c);
|
---|
| 682 | mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
|
---|
| 683 | }
|
---|
| 684 | break;
|
---|
| 685 |
|
---|
| 686 | case MRB_TT_CLASS:
|
---|
| 687 | case MRB_TT_MODULE:
|
---|
| 688 | case MRB_TT_SCLASS:
|
---|
| 689 | {
|
---|
| 690 | struct RClass *c = (struct RClass*)obj;
|
---|
| 691 |
|
---|
| 692 | mrb_gc_mark_mt(mrb, c);
|
---|
| 693 | mrb_gc_mark(mrb, (struct RBasic*)c->super);
|
---|
| 694 | }
|
---|
| 695 | /* fall through */
|
---|
| 696 |
|
---|
| 697 | case MRB_TT_OBJECT:
|
---|
| 698 | case MRB_TT_DATA:
|
---|
| 699 | case MRB_TT_EXCEPTION:
|
---|
| 700 | mrb_gc_mark_iv(mrb, (struct RObject*)obj);
|
---|
| 701 | break;
|
---|
| 702 |
|
---|
| 703 | case MRB_TT_PROC:
|
---|
| 704 | {
|
---|
| 705 | struct RProc *p = (struct RProc*)obj;
|
---|
| 706 |
|
---|
[439] | 707 | mrb_gc_mark(mrb, (struct RBasic*)p->upper);
|
---|
| 708 | mrb_gc_mark(mrb, (struct RBasic*)p->e.env);
|
---|
[270] | 709 | }
|
---|
| 710 | break;
|
---|
| 711 |
|
---|
| 712 | case MRB_TT_ENV:
|
---|
| 713 | {
|
---|
| 714 | struct REnv *e = (struct REnv*)obj;
|
---|
[331] | 715 | mrb_int i, len;
|
---|
[270] | 716 |
|
---|
[439] | 717 | if (MRB_ENV_STACK_SHARED_P(e) && e->cxt && e->cxt->fib) {
|
---|
| 718 | mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib);
|
---|
[270] | 719 | }
|
---|
[331] | 720 | len = MRB_ENV_STACK_LEN(e);
|
---|
| 721 | for (i=0; i<len; i++) {
|
---|
| 722 | mrb_gc_mark_value(mrb, e->stack[i]);
|
---|
| 723 | }
|
---|
[270] | 724 | }
|
---|
| 725 | break;
|
---|
| 726 |
|
---|
| 727 | case MRB_TT_FIBER:
|
---|
| 728 | {
|
---|
| 729 | struct mrb_context *c = ((struct RFiber*)obj)->cxt;
|
---|
| 730 |
|
---|
| 731 | if (c) mark_context(mrb, c);
|
---|
| 732 | }
|
---|
| 733 | break;
|
---|
| 734 |
|
---|
| 735 | case MRB_TT_ARRAY:
|
---|
| 736 | {
|
---|
| 737 | struct RArray *a = (struct RArray*)obj;
|
---|
| 738 | size_t i, e;
|
---|
| 739 |
|
---|
[439] | 740 | for (i=0,e=ARY_LEN(a); i<e; i++) {
|
---|
| 741 | mrb_gc_mark_value(mrb, ARY_PTR(a)[i]);
|
---|
[270] | 742 | }
|
---|
| 743 | }
|
---|
| 744 | break;
|
---|
| 745 |
|
---|
| 746 | case MRB_TT_HASH:
|
---|
| 747 | mrb_gc_mark_iv(mrb, (struct RObject*)obj);
|
---|
| 748 | mrb_gc_mark_hash(mrb, (struct RHash*)obj);
|
---|
| 749 | break;
|
---|
| 750 |
|
---|
| 751 | case MRB_TT_STRING:
|
---|
[439] | 752 | if (RSTR_FSHARED_P(obj)) {
|
---|
| 753 | struct RString *s = (struct RString*)obj;
|
---|
| 754 | mrb_gc_mark(mrb, (struct RBasic*)s->as.heap.aux.fshared);
|
---|
| 755 | }
|
---|
[270] | 756 | break;
|
---|
| 757 |
|
---|
| 758 | case MRB_TT_RANGE:
|
---|
[439] | 759 | mrb_gc_mark_range(mrb, (struct RRange*)obj);
|
---|
[270] | 760 | break;
|
---|
| 761 |
|
---|
| 762 | default:
|
---|
| 763 | break;
|
---|
| 764 | }
|
---|
| 765 | }
|
---|
| 766 |
|
---|
| 767 | MRB_API void
|
---|
| 768 | mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
|
---|
| 769 | {
|
---|
| 770 | if (obj == 0) return;
|
---|
| 771 | if (!is_white(obj)) return;
|
---|
| 772 | mrb_assert((obj)->tt != MRB_TT_FREE);
|
---|
| 773 | add_gray_list(mrb, &mrb->gc, obj);
|
---|
| 774 | }
|
---|
| 775 |
|
---|
| 776 | static void
|
---|
[331] | 777 | obj_free(mrb_state *mrb, struct RBasic *obj, int end)
|
---|
[270] | 778 | {
|
---|
[331] | 779 | DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt));
|
---|
[270] | 780 | switch (obj->tt) {
|
---|
| 781 | /* immediate - no mark */
|
---|
| 782 | case MRB_TT_TRUE:
|
---|
| 783 | case MRB_TT_FIXNUM:
|
---|
| 784 | case MRB_TT_SYMBOL:
|
---|
| 785 | /* cannot happen */
|
---|
| 786 | return;
|
---|
| 787 |
|
---|
[439] | 788 | #ifndef MRB_WITHOUT_FLOAT
|
---|
[270] | 789 | case MRB_TT_FLOAT:
|
---|
| 790 | #ifdef MRB_WORD_BOXING
|
---|
| 791 | break;
|
---|
| 792 | #else
|
---|
| 793 | return;
|
---|
| 794 | #endif
|
---|
[439] | 795 | #endif
|
---|
[270] | 796 |
|
---|
| 797 | case MRB_TT_OBJECT:
|
---|
[331] | 798 | mrb_gc_free_iv(mrb, (struct RObject*)obj);
|
---|
| 799 | break;
|
---|
| 800 |
|
---|
[270] | 801 | case MRB_TT_EXCEPTION:
|
---|
| 802 | mrb_gc_free_iv(mrb, (struct RObject*)obj);
|
---|
| 803 | break;
|
---|
| 804 |
|
---|
| 805 | case MRB_TT_CLASS:
|
---|
| 806 | case MRB_TT_MODULE:
|
---|
| 807 | case MRB_TT_SCLASS:
|
---|
| 808 | mrb_gc_free_mt(mrb, (struct RClass*)obj);
|
---|
| 809 | mrb_gc_free_iv(mrb, (struct RObject*)obj);
|
---|
[439] | 810 | mrb_mc_clear_by_class(mrb, (struct RClass*)obj);
|
---|
[270] | 811 | break;
|
---|
| 812 | case MRB_TT_ICLASS:
|
---|
[439] | 813 | if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN))
|
---|
[270] | 814 | mrb_gc_free_mt(mrb, (struct RClass*)obj);
|
---|
[439] | 815 | mrb_mc_clear_by_class(mrb, (struct RClass*)obj);
|
---|
[270] | 816 | break;
|
---|
| 817 | case MRB_TT_ENV:
|
---|
| 818 | {
|
---|
| 819 | struct REnv *e = (struct REnv*)obj;
|
---|
| 820 |
|
---|
[331] | 821 | if (MRB_ENV_STACK_SHARED_P(e)) {
|
---|
| 822 | /* cannot be freed */
|
---|
[439] | 823 | e->stack = NULL;
|
---|
| 824 | break;
|
---|
[270] | 825 | }
|
---|
[331] | 826 | mrb_free(mrb, e->stack);
|
---|
| 827 | e->stack = NULL;
|
---|
[270] | 828 | }
|
---|
| 829 | break;
|
---|
| 830 |
|
---|
| 831 | case MRB_TT_FIBER:
|
---|
| 832 | {
|
---|
| 833 | struct mrb_context *c = ((struct RFiber*)obj)->cxt;
|
---|
| 834 |
|
---|
[439] | 835 | if (c && c != mrb->root_c) {
|
---|
| 836 | if (!end && c->status != MRB_FIBER_TERMINATED) {
|
---|
| 837 | mrb_callinfo *ci = c->ci;
|
---|
| 838 | mrb_callinfo *ce = c->cibase;
|
---|
[331] | 839 |
|
---|
[439] | 840 | while (ce <= ci) {
|
---|
| 841 | struct REnv *e = ci->env;
|
---|
| 842 | if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) &&
|
---|
| 843 | e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
|
---|
| 844 | mrb_env_unshare(mrb, e);
|
---|
| 845 | }
|
---|
| 846 | ci--;
|
---|
[331] | 847 | }
|
---|
| 848 | }
|
---|
[270] | 849 | mrb_free_context(mrb, c);
|
---|
[331] | 850 | }
|
---|
[270] | 851 | }
|
---|
| 852 | break;
|
---|
| 853 |
|
---|
| 854 | case MRB_TT_ARRAY:
|
---|
| 855 | if (ARY_SHARED_P(obj))
|
---|
[439] | 856 | mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared);
|
---|
| 857 | else if (!ARY_EMBED_P(obj))
|
---|
| 858 | mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr);
|
---|
[270] | 859 | break;
|
---|
| 860 |
|
---|
| 861 | case MRB_TT_HASH:
|
---|
| 862 | mrb_gc_free_iv(mrb, (struct RObject*)obj);
|
---|
| 863 | mrb_gc_free_hash(mrb, (struct RHash*)obj);
|
---|
| 864 | break;
|
---|
| 865 |
|
---|
| 866 | case MRB_TT_STRING:
|
---|
| 867 | mrb_gc_free_str(mrb, (struct RString*)obj);
|
---|
| 868 | break;
|
---|
| 869 |
|
---|
| 870 | case MRB_TT_PROC:
|
---|
| 871 | {
|
---|
| 872 | struct RProc *p = (struct RProc*)obj;
|
---|
| 873 |
|
---|
| 874 | if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
|
---|
[439] | 875 | mrb_irep *irep = p->body.irep;
|
---|
| 876 | if (end) {
|
---|
| 877 | mrb_irep_cutref(mrb, irep);
|
---|
| 878 | }
|
---|
| 879 | mrb_irep_decref(mrb, irep);
|
---|
[270] | 880 | }
|
---|
| 881 | }
|
---|
| 882 | break;
|
---|
| 883 |
|
---|
| 884 | case MRB_TT_RANGE:
|
---|
[439] | 885 | mrb_gc_free_range(mrb, ((struct RRange*)obj));
|
---|
[270] | 886 | break;
|
---|
| 887 |
|
---|
| 888 | case MRB_TT_DATA:
|
---|
| 889 | {
|
---|
| 890 | struct RData *d = (struct RData*)obj;
|
---|
| 891 | if (d->type && d->type->dfree) {
|
---|
| 892 | d->type->dfree(mrb, d->data);
|
---|
| 893 | }
|
---|
| 894 | mrb_gc_free_iv(mrb, (struct RObject*)obj);
|
---|
| 895 | }
|
---|
| 896 | break;
|
---|
| 897 |
|
---|
| 898 | default:
|
---|
| 899 | break;
|
---|
| 900 | }
|
---|
| 901 | obj->tt = MRB_TT_FREE;
|
---|
| 902 | }
|
---|
| 903 |
|
---|
| 904 | static void
|
---|
| 905 | root_scan_phase(mrb_state *mrb, mrb_gc *gc)
|
---|
| 906 | {
|
---|
[331] | 907 | int i, e;
|
---|
[270] | 908 |
|
---|
| 909 | if (!is_minor_gc(gc)) {
|
---|
| 910 | gc->gray_list = NULL;
|
---|
| 911 | gc->atomic_gray_list = NULL;
|
---|
| 912 | }
|
---|
| 913 |
|
---|
| 914 | mrb_gc_mark_gv(mrb);
|
---|
| 915 | /* mark arena */
|
---|
| 916 | for (i=0,e=gc->arena_idx; i<e; i++) {
|
---|
| 917 | mrb_gc_mark(mrb, gc->arena[i]);
|
---|
| 918 | }
|
---|
| 919 | /* mark class hierarchy */
|
---|
| 920 | mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class);
|
---|
[331] | 921 |
|
---|
| 922 | /* mark built-in classes */
|
---|
| 923 | mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class);
|
---|
| 924 | mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class);
|
---|
| 925 | mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class);
|
---|
| 926 | mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class);
|
---|
| 927 | mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class);
|
---|
| 928 | mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
|
---|
[439] | 929 | mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class);
|
---|
[331] | 930 |
|
---|
[439] | 931 | #ifndef MRB_WITHOUT_FLOAT
|
---|
[331] | 932 | mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class);
|
---|
[439] | 933 | #endif
|
---|
[331] | 934 | mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class);
|
---|
| 935 | mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class);
|
---|
| 936 | mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class);
|
---|
| 937 | mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class);
|
---|
| 938 | mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class);
|
---|
| 939 | mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module);
|
---|
| 940 |
|
---|
| 941 | mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class);
|
---|
| 942 | mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class);
|
---|
| 943 |
|
---|
[270] | 944 | /* mark top_self */
|
---|
| 945 | mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self);
|
---|
| 946 | /* mark exception */
|
---|
| 947 | mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
|
---|
| 948 | /* mark pre-allocated exception */
|
---|
| 949 | mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err);
|
---|
[331] | 950 | mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err);
|
---|
| 951 | #ifdef MRB_GC_FIXED_ARENA
|
---|
| 952 | mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err);
|
---|
| 953 | #endif
|
---|
[270] | 954 |
|
---|
[331] | 955 | mark_context(mrb, mrb->c);
|
---|
[270] | 956 | if (mrb->root_c != mrb->c) {
|
---|
[331] | 957 | mark_context(mrb, mrb->root_c);
|
---|
[270] | 958 | }
|
---|
| 959 | }
|
---|
| 960 |
|
---|
| 961 | static size_t
|
---|
| 962 | gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
|
---|
| 963 | {
|
---|
| 964 | size_t children = 0;
|
---|
| 965 |
|
---|
| 966 | gc_mark_children(mrb, gc, obj);
|
---|
| 967 |
|
---|
| 968 | switch (obj->tt) {
|
---|
| 969 | case MRB_TT_ICLASS:
|
---|
| 970 | children++;
|
---|
| 971 | break;
|
---|
| 972 |
|
---|
| 973 | case MRB_TT_CLASS:
|
---|
| 974 | case MRB_TT_SCLASS:
|
---|
| 975 | case MRB_TT_MODULE:
|
---|
| 976 | {
|
---|
| 977 | struct RClass *c = (struct RClass*)obj;
|
---|
| 978 |
|
---|
| 979 | children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
|
---|
| 980 | children += mrb_gc_mark_mt_size(mrb, c);
|
---|
| 981 | children++;
|
---|
| 982 | }
|
---|
| 983 | break;
|
---|
| 984 |
|
---|
| 985 | case MRB_TT_OBJECT:
|
---|
| 986 | case MRB_TT_DATA:
|
---|
| 987 | case MRB_TT_EXCEPTION:
|
---|
| 988 | children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
|
---|
| 989 | break;
|
---|
| 990 |
|
---|
| 991 | case MRB_TT_ENV:
|
---|
[439] | 992 | children += MRB_ENV_STACK_LEN(obj);
|
---|
[270] | 993 | break;
|
---|
| 994 |
|
---|
| 995 | case MRB_TT_FIBER:
|
---|
| 996 | {
|
---|
| 997 | struct mrb_context *c = ((struct RFiber*)obj)->cxt;
|
---|
| 998 | size_t i;
|
---|
| 999 | mrb_callinfo *ci;
|
---|
| 1000 |
|
---|
[439] | 1001 | if (!c || c->status == MRB_FIBER_TERMINATED) break;
|
---|
| 1002 |
|
---|
[270] | 1003 | /* mark stack */
|
---|
| 1004 | i = c->stack - c->stbase;
|
---|
[439] | 1005 |
|
---|
| 1006 | if (c->ci) {
|
---|
| 1007 | i += ci_nregs(c->ci);
|
---|
| 1008 | }
|
---|
[270] | 1009 | if (c->stbase + i > c->stend) i = c->stend - c->stbase;
|
---|
| 1010 | children += i;
|
---|
| 1011 |
|
---|
| 1012 | /* mark ensure stack */
|
---|
[331] | 1013 | children += c->eidx;
|
---|
[270] | 1014 |
|
---|
| 1015 | /* mark closure */
|
---|
| 1016 | if (c->cibase) {
|
---|
| 1017 | for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++)
|
---|
| 1018 | ;
|
---|
| 1019 | }
|
---|
| 1020 | children += i;
|
---|
| 1021 | }
|
---|
| 1022 | break;
|
---|
| 1023 |
|
---|
| 1024 | case MRB_TT_ARRAY:
|
---|
| 1025 | {
|
---|
| 1026 | struct RArray *a = (struct RArray*)obj;
|
---|
[439] | 1027 | children += ARY_LEN(a);
|
---|
[270] | 1028 | }
|
---|
| 1029 | break;
|
---|
| 1030 |
|
---|
| 1031 | case MRB_TT_HASH:
|
---|
| 1032 | children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
|
---|
| 1033 | children += mrb_gc_mark_hash_size(mrb, (struct RHash*)obj);
|
---|
| 1034 | break;
|
---|
| 1035 |
|
---|
| 1036 | case MRB_TT_PROC:
|
---|
| 1037 | case MRB_TT_RANGE:
|
---|
| 1038 | children+=2;
|
---|
| 1039 | break;
|
---|
| 1040 |
|
---|
| 1041 | default:
|
---|
| 1042 | break;
|
---|
| 1043 | }
|
---|
| 1044 | return children;
|
---|
| 1045 | }
|
---|
| 1046 |
|
---|
| 1047 |
|
---|
| 1048 | static void
|
---|
| 1049 | gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) {
|
---|
| 1050 | while (gc->gray_list) {
|
---|
| 1051 | if (is_gray(gc->gray_list))
|
---|
| 1052 | gc_mark_children(mrb, gc, gc->gray_list);
|
---|
| 1053 | else
|
---|
| 1054 | gc->gray_list = gc->gray_list->gcnext;
|
---|
| 1055 | }
|
---|
| 1056 | }
|
---|
| 1057 |
|
---|
| 1058 |
|
---|
| 1059 | static size_t
|
---|
| 1060 | incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
|
---|
| 1061 | {
|
---|
| 1062 | size_t tried_marks = 0;
|
---|
| 1063 |
|
---|
| 1064 | while (gc->gray_list && tried_marks < limit) {
|
---|
| 1065 | tried_marks += gc_gray_mark(mrb, gc, gc->gray_list);
|
---|
| 1066 | }
|
---|
| 1067 |
|
---|
| 1068 | return tried_marks;
|
---|
| 1069 | }
|
---|
| 1070 |
|
---|
| 1071 | static void
|
---|
| 1072 | final_marking_phase(mrb_state *mrb, mrb_gc *gc)
|
---|
| 1073 | {
|
---|
[331] | 1074 | int i, e;
|
---|
| 1075 |
|
---|
| 1076 | /* mark arena */
|
---|
| 1077 | for (i=0,e=gc->arena_idx; i<e; i++) {
|
---|
| 1078 | mrb_gc_mark(mrb, gc->arena[i]);
|
---|
| 1079 | }
|
---|
| 1080 | mrb_gc_mark_gv(mrb);
|
---|
| 1081 | mark_context(mrb, mrb->c);
|
---|
| 1082 | mark_context(mrb, mrb->root_c);
|
---|
| 1083 | mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
|
---|
[270] | 1084 | gc_mark_gray_list(mrb, gc);
|
---|
| 1085 | mrb_assert(gc->gray_list == NULL);
|
---|
| 1086 | gc->gray_list = gc->atomic_gray_list;
|
---|
| 1087 | gc->atomic_gray_list = NULL;
|
---|
| 1088 | gc_mark_gray_list(mrb, gc);
|
---|
| 1089 | mrb_assert(gc->gray_list == NULL);
|
---|
| 1090 | }
|
---|
| 1091 |
|
---|
| 1092 | static void
|
---|
| 1093 | prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc)
|
---|
| 1094 | {
|
---|
| 1095 | gc->state = MRB_GC_STATE_SWEEP;
|
---|
| 1096 | gc->sweeps = gc->heaps;
|
---|
| 1097 | gc->live_after_mark = gc->live;
|
---|
| 1098 | }
|
---|
| 1099 |
|
---|
| 1100 | static size_t
|
---|
| 1101 | incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
|
---|
| 1102 | {
|
---|
| 1103 | mrb_heap_page *page = gc->sweeps;
|
---|
| 1104 | size_t tried_sweep = 0;
|
---|
| 1105 |
|
---|
| 1106 | while (page && (tried_sweep < limit)) {
|
---|
| 1107 | RVALUE *p = objects(page);
|
---|
| 1108 | RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
|
---|
| 1109 | size_t freed = 0;
|
---|
| 1110 | mrb_bool dead_slot = TRUE;
|
---|
| 1111 | mrb_bool full = (page->freelist == NULL);
|
---|
| 1112 |
|
---|
| 1113 | if (is_minor_gc(gc) && page->old) {
|
---|
| 1114 | /* skip a slot which doesn't contain any young object */
|
---|
| 1115 | p = e;
|
---|
| 1116 | dead_slot = FALSE;
|
---|
| 1117 | }
|
---|
| 1118 | while (p<e) {
|
---|
| 1119 | if (is_dead(gc, &p->as.basic)) {
|
---|
| 1120 | if (p->as.basic.tt != MRB_TT_FREE) {
|
---|
[331] | 1121 | obj_free(mrb, &p->as.basic, FALSE);
|
---|
| 1122 | if (p->as.basic.tt == MRB_TT_FREE) {
|
---|
| 1123 | p->as.free.next = page->freelist;
|
---|
| 1124 | page->freelist = (struct RBasic*)p;
|
---|
| 1125 | freed++;
|
---|
| 1126 | }
|
---|
| 1127 | else {
|
---|
| 1128 | dead_slot = FALSE;
|
---|
| 1129 | }
|
---|
[270] | 1130 | }
|
---|
| 1131 | }
|
---|
| 1132 | else {
|
---|
| 1133 | if (!is_generational(gc))
|
---|
| 1134 | paint_partial_white(gc, &p->as.basic); /* next gc target */
|
---|
[331] | 1135 | dead_slot = FALSE;
|
---|
[270] | 1136 | }
|
---|
| 1137 | p++;
|
---|
| 1138 | }
|
---|
| 1139 |
|
---|
| 1140 | /* free dead slot */
|
---|
| 1141 | if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) {
|
---|
| 1142 | mrb_heap_page *next = page->next;
|
---|
| 1143 |
|
---|
| 1144 | unlink_heap_page(gc, page);
|
---|
| 1145 | unlink_free_heap_page(gc, page);
|
---|
| 1146 | mrb_free(mrb, page);
|
---|
| 1147 | page = next;
|
---|
| 1148 | }
|
---|
| 1149 | else {
|
---|
| 1150 | if (full && freed > 0) {
|
---|
| 1151 | link_free_heap_page(gc, page);
|
---|
| 1152 | }
|
---|
| 1153 | if (page->freelist == NULL && is_minor_gc(gc))
|
---|
| 1154 | page->old = TRUE;
|
---|
| 1155 | else
|
---|
| 1156 | page->old = FALSE;
|
---|
| 1157 | page = page->next;
|
---|
| 1158 | }
|
---|
| 1159 | tried_sweep += MRB_HEAP_PAGE_SIZE;
|
---|
| 1160 | gc->live -= freed;
|
---|
| 1161 | gc->live_after_mark -= freed;
|
---|
| 1162 | }
|
---|
| 1163 | gc->sweeps = page;
|
---|
| 1164 | return tried_sweep;
|
---|
| 1165 | }
|
---|
| 1166 |
|
---|
| 1167 | static size_t
|
---|
| 1168 | incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit)
|
---|
| 1169 | {
|
---|
| 1170 | switch (gc->state) {
|
---|
| 1171 | case MRB_GC_STATE_ROOT:
|
---|
| 1172 | root_scan_phase(mrb, gc);
|
---|
| 1173 | gc->state = MRB_GC_STATE_MARK;
|
---|
| 1174 | flip_white_part(gc);
|
---|
| 1175 | return 0;
|
---|
| 1176 | case MRB_GC_STATE_MARK:
|
---|
| 1177 | if (gc->gray_list) {
|
---|
| 1178 | return incremental_marking_phase(mrb, gc, limit);
|
---|
| 1179 | }
|
---|
| 1180 | else {
|
---|
| 1181 | final_marking_phase(mrb, gc);
|
---|
| 1182 | prepare_incremental_sweep(mrb, gc);
|
---|
| 1183 | return 0;
|
---|
| 1184 | }
|
---|
| 1185 | case MRB_GC_STATE_SWEEP: {
|
---|
| 1186 | size_t tried_sweep = 0;
|
---|
| 1187 | tried_sweep = incremental_sweep_phase(mrb, gc, limit);
|
---|
| 1188 | if (tried_sweep == 0)
|
---|
| 1189 | gc->state = MRB_GC_STATE_ROOT;
|
---|
| 1190 | return tried_sweep;
|
---|
| 1191 | }
|
---|
| 1192 | default:
|
---|
| 1193 | /* unknown state */
|
---|
| 1194 | mrb_assert(0);
|
---|
| 1195 | return 0;
|
---|
| 1196 | }
|
---|
| 1197 | }
|
---|
| 1198 |
|
---|
| 1199 | static void
|
---|
| 1200 | incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state)
|
---|
| 1201 | {
|
---|
| 1202 | do {
|
---|
| 1203 | incremental_gc(mrb, gc, SIZE_MAX);
|
---|
| 1204 | } while (gc->state != to_state);
|
---|
| 1205 | }
|
---|
| 1206 |
|
---|
| 1207 | static void
|
---|
| 1208 | incremental_gc_step(mrb_state *mrb, mrb_gc *gc)
|
---|
| 1209 | {
|
---|
| 1210 | size_t limit = 0, result = 0;
|
---|
| 1211 | limit = (GC_STEP_SIZE/100) * gc->step_ratio;
|
---|
| 1212 | while (result < limit) {
|
---|
| 1213 | result += incremental_gc(mrb, gc, limit);
|
---|
| 1214 | if (gc->state == MRB_GC_STATE_ROOT)
|
---|
| 1215 | break;
|
---|
| 1216 | }
|
---|
| 1217 |
|
---|
| 1218 | gc->threshold = gc->live + GC_STEP_SIZE;
|
---|
| 1219 | }
|
---|
| 1220 |
|
---|
| 1221 | static void
|
---|
| 1222 | clear_all_old(mrb_state *mrb, mrb_gc *gc)
|
---|
| 1223 | {
|
---|
| 1224 | mrb_bool origin_mode = gc->generational;
|
---|
| 1225 |
|
---|
| 1226 | mrb_assert(is_generational(gc));
|
---|
| 1227 | if (is_major_gc(gc)) {
|
---|
| 1228 | /* finish the half baked GC */
|
---|
| 1229 | incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
|
---|
| 1230 | }
|
---|
| 1231 |
|
---|
| 1232 | /* Sweep the dead objects, then reset all the live objects
|
---|
| 1233 | * (including all the old objects, of course) to white. */
|
---|
| 1234 | gc->generational = FALSE;
|
---|
| 1235 | prepare_incremental_sweep(mrb, gc);
|
---|
| 1236 | incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
|
---|
| 1237 | gc->generational = origin_mode;
|
---|
| 1238 |
|
---|
| 1239 | /* The gray objects have already been painted as white */
|
---|
| 1240 | gc->atomic_gray_list = gc->gray_list = NULL;
|
---|
| 1241 | }
|
---|
| 1242 |
|
---|
| 1243 | MRB_API void
|
---|
| 1244 | mrb_incremental_gc(mrb_state *mrb)
|
---|
| 1245 | {
|
---|
| 1246 | mrb_gc *gc = &mrb->gc;
|
---|
| 1247 |
|
---|
[331] | 1248 | if (gc->disabled || gc->iterating) return;
|
---|
[270] | 1249 |
|
---|
| 1250 | GC_INVOKE_TIME_REPORT("mrb_incremental_gc()");
|
---|
| 1251 | GC_TIME_START;
|
---|
| 1252 |
|
---|
| 1253 | if (is_minor_gc(gc)) {
|
---|
| 1254 | incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
|
---|
| 1255 | }
|
---|
| 1256 | else {
|
---|
| 1257 | incremental_gc_step(mrb, gc);
|
---|
| 1258 | }
|
---|
| 1259 |
|
---|
| 1260 | if (gc->state == MRB_GC_STATE_ROOT) {
|
---|
| 1261 | mrb_assert(gc->live >= gc->live_after_mark);
|
---|
| 1262 | gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
|
---|
| 1263 | if (gc->threshold < GC_STEP_SIZE) {
|
---|
| 1264 | gc->threshold = GC_STEP_SIZE;
|
---|
| 1265 | }
|
---|
| 1266 |
|
---|
| 1267 | if (is_major_gc(gc)) {
|
---|
[439] | 1268 | size_t threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
|
---|
| 1269 |
|
---|
[270] | 1270 | gc->full = FALSE;
|
---|
[439] | 1271 | if (threshold < MAJOR_GC_TOOMANY) {
|
---|
| 1272 | gc->majorgc_old_threshold = threshold;
|
---|
| 1273 | }
|
---|
| 1274 | else {
|
---|
| 1275 | /* too many objects allocated during incremental GC, */
|
---|
| 1276 | /* instead of increasing threshold, invoke full GC. */
|
---|
| 1277 | mrb_full_gc(mrb);
|
---|
| 1278 | }
|
---|
[270] | 1279 | }
|
---|
| 1280 | else if (is_minor_gc(gc)) {
|
---|
| 1281 | if (gc->live > gc->majorgc_old_threshold) {
|
---|
| 1282 | clear_all_old(mrb, gc);
|
---|
| 1283 | gc->full = TRUE;
|
---|
| 1284 | }
|
---|
| 1285 | }
|
---|
| 1286 | }
|
---|
| 1287 |
|
---|
| 1288 | GC_TIME_STOP_AND_REPORT;
|
---|
| 1289 | }
|
---|
| 1290 |
|
---|
| 1291 | /* Perform a full gc cycle */
|
---|
| 1292 | MRB_API void
|
---|
| 1293 | mrb_full_gc(mrb_state *mrb)
|
---|
| 1294 | {
|
---|
| 1295 | mrb_gc *gc = &mrb->gc;
|
---|
| 1296 |
|
---|
[331] | 1297 | if (gc->disabled || gc->iterating) return;
|
---|
[270] | 1298 |
|
---|
| 1299 | GC_INVOKE_TIME_REPORT("mrb_full_gc()");
|
---|
| 1300 | GC_TIME_START;
|
---|
| 1301 |
|
---|
| 1302 | if (is_generational(gc)) {
|
---|
| 1303 | /* clear all the old objects back to young */
|
---|
| 1304 | clear_all_old(mrb, gc);
|
---|
| 1305 | gc->full = TRUE;
|
---|
| 1306 | }
|
---|
| 1307 | else if (gc->state != MRB_GC_STATE_ROOT) {
|
---|
| 1308 | /* finish half baked GC cycle */
|
---|
| 1309 | incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
|
---|
| 1310 | }
|
---|
| 1311 |
|
---|
| 1312 | incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
|
---|
| 1313 | gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
|
---|
| 1314 |
|
---|
| 1315 | if (is_generational(gc)) {
|
---|
[439] | 1316 | gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
|
---|
[270] | 1317 | gc->full = FALSE;
|
---|
| 1318 | }
|
---|
| 1319 |
|
---|
| 1320 | GC_TIME_STOP_AND_REPORT;
|
---|
| 1321 | }
|
---|
| 1322 |
|
---|
| 1323 | MRB_API void
|
---|
| 1324 | mrb_garbage_collect(mrb_state *mrb)
|
---|
| 1325 | {
|
---|
| 1326 | mrb_full_gc(mrb);
|
---|
| 1327 | }
|
---|
| 1328 |
|
---|
| 1329 | /*
|
---|
| 1330 | * Field write barrier
|
---|
| 1331 | * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray).
|
---|
| 1332 | */
|
---|
| 1333 |
|
---|
| 1334 | MRB_API void
|
---|
| 1335 | mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value)
|
---|
| 1336 | {
|
---|
| 1337 | mrb_gc *gc = &mrb->gc;
|
---|
| 1338 |
|
---|
| 1339 | if (!is_black(obj)) return;
|
---|
| 1340 | if (!is_white(value)) return;
|
---|
| 1341 |
|
---|
| 1342 | mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj)));
|
---|
| 1343 | mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
|
---|
| 1344 |
|
---|
| 1345 | if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) {
|
---|
| 1346 | add_gray_list(mrb, gc, value);
|
---|
| 1347 | }
|
---|
| 1348 | else {
|
---|
| 1349 | mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
|
---|
| 1350 | paint_partial_white(gc, obj); /* for never write barriers */
|
---|
| 1351 | }
|
---|
| 1352 | }
|
---|
| 1353 |
|
---|
| 1354 | /*
|
---|
| 1355 | * Write barrier
|
---|
| 1356 | * Paint obj(Black) to obj(Gray).
|
---|
| 1357 | *
|
---|
| 1358 | * The object that is painted gray will be traversed atomically in final
|
---|
| 1359 | * mark phase. So you use this write barrier if it's frequency written spot.
|
---|
| 1360 | * e.g. Set element on Array.
|
---|
| 1361 | */
|
---|
| 1362 |
|
---|
| 1363 | MRB_API void
|
---|
| 1364 | mrb_write_barrier(mrb_state *mrb, struct RBasic *obj)
|
---|
| 1365 | {
|
---|
| 1366 | mrb_gc *gc = &mrb->gc;
|
---|
| 1367 |
|
---|
| 1368 | if (!is_black(obj)) return;
|
---|
| 1369 |
|
---|
| 1370 | mrb_assert(!is_dead(gc, obj));
|
---|
| 1371 | mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
|
---|
| 1372 | paint_gray(obj);
|
---|
| 1373 | obj->gcnext = gc->atomic_gray_list;
|
---|
| 1374 | gc->atomic_gray_list = obj;
|
---|
| 1375 | }
|
---|
| 1376 |
|
---|
| 1377 | /*
|
---|
| 1378 | * call-seq:
|
---|
| 1379 | * GC.start -> nil
|
---|
| 1380 | *
|
---|
| 1381 | * Initiates full garbage collection.
|
---|
| 1382 | *
|
---|
| 1383 | */
|
---|
| 1384 |
|
---|
| 1385 | static mrb_value
|
---|
| 1386 | gc_start(mrb_state *mrb, mrb_value obj)
|
---|
| 1387 | {
|
---|
| 1388 | mrb_full_gc(mrb);
|
---|
| 1389 | return mrb_nil_value();
|
---|
| 1390 | }
|
---|
| 1391 |
|
---|
| 1392 | /*
|
---|
| 1393 | * call-seq:
|
---|
| 1394 | * GC.enable -> true or false
|
---|
| 1395 | *
|
---|
| 1396 | * Enables garbage collection, returning <code>true</code> if garbage
|
---|
| 1397 | * collection was previously disabled.
|
---|
| 1398 | *
|
---|
| 1399 | * GC.disable #=> false
|
---|
| 1400 | * GC.enable #=> true
|
---|
| 1401 | * GC.enable #=> false
|
---|
| 1402 | *
|
---|
| 1403 | */
|
---|
| 1404 |
|
---|
| 1405 | static mrb_value
|
---|
| 1406 | gc_enable(mrb_state *mrb, mrb_value obj)
|
---|
| 1407 | {
|
---|
| 1408 | mrb_bool old = mrb->gc.disabled;
|
---|
| 1409 |
|
---|
| 1410 | mrb->gc.disabled = FALSE;
|
---|
| 1411 |
|
---|
| 1412 | return mrb_bool_value(old);
|
---|
| 1413 | }
|
---|
| 1414 |
|
---|
| 1415 | /*
|
---|
| 1416 | * call-seq:
|
---|
| 1417 | * GC.disable -> true or false
|
---|
| 1418 | *
|
---|
| 1419 | * Disables garbage collection, returning <code>true</code> if garbage
|
---|
| 1420 | * collection was already disabled.
|
---|
| 1421 | *
|
---|
| 1422 | * GC.disable #=> false
|
---|
| 1423 | * GC.disable #=> true
|
---|
| 1424 | *
|
---|
| 1425 | */
|
---|
| 1426 |
|
---|
| 1427 | static mrb_value
|
---|
| 1428 | gc_disable(mrb_state *mrb, mrb_value obj)
|
---|
| 1429 | {
|
---|
| 1430 | mrb_bool old = mrb->gc.disabled;
|
---|
| 1431 |
|
---|
| 1432 | mrb->gc.disabled = TRUE;
|
---|
| 1433 |
|
---|
| 1434 | return mrb_bool_value(old);
|
---|
| 1435 | }
|
---|
| 1436 |
|
---|
| 1437 | /*
|
---|
| 1438 | * call-seq:
|
---|
| 1439 | * GC.interval_ratio -> fixnum
|
---|
| 1440 | *
|
---|
| 1441 | * Returns ratio of GC interval. Default value is 200(%).
|
---|
| 1442 | *
|
---|
| 1443 | */
|
---|
| 1444 |
|
---|
| 1445 | static mrb_value
|
---|
| 1446 | gc_interval_ratio_get(mrb_state *mrb, mrb_value obj)
|
---|
| 1447 | {
|
---|
| 1448 | return mrb_fixnum_value(mrb->gc.interval_ratio);
|
---|
| 1449 | }
|
---|
| 1450 |
|
---|
| 1451 | /*
|
---|
| 1452 | * call-seq:
|
---|
| 1453 | * GC.interval_ratio = fixnum -> nil
|
---|
| 1454 | *
|
---|
| 1455 | * Updates ratio of GC interval. Default value is 200(%).
|
---|
| 1456 | * GC start as soon as after end all step of GC if you set 100(%).
|
---|
| 1457 | *
|
---|
| 1458 | */
|
---|
| 1459 |
|
---|
| 1460 | static mrb_value
|
---|
| 1461 | gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
|
---|
| 1462 | {
|
---|
| 1463 | mrb_int ratio;
|
---|
| 1464 |
|
---|
| 1465 | mrb_get_args(mrb, "i", &ratio);
|
---|
[439] | 1466 | mrb->gc.interval_ratio = (int)ratio;
|
---|
[270] | 1467 | return mrb_nil_value();
|
---|
| 1468 | }
|
---|
| 1469 |
|
---|
| 1470 | /*
|
---|
| 1471 | * call-seq:
|
---|
| 1472 | * GC.step_ratio -> fixnum
|
---|
| 1473 | *
|
---|
| 1474 | * Returns step span ratio of Incremental GC. Default value is 200(%).
|
---|
| 1475 | *
|
---|
| 1476 | */
|
---|
| 1477 |
|
---|
| 1478 | static mrb_value
|
---|
| 1479 | gc_step_ratio_get(mrb_state *mrb, mrb_value obj)
|
---|
| 1480 | {
|
---|
| 1481 | return mrb_fixnum_value(mrb->gc.step_ratio);
|
---|
| 1482 | }
|
---|
| 1483 |
|
---|
| 1484 | /*
|
---|
| 1485 | * call-seq:
|
---|
| 1486 | * GC.step_ratio = fixnum -> nil
|
---|
| 1487 | *
|
---|
| 1488 | * Updates step span ratio of Incremental GC. Default value is 200(%).
|
---|
| 1489 | * 1 step of incrementalGC becomes long if a rate is big.
|
---|
| 1490 | *
|
---|
| 1491 | */
|
---|
| 1492 |
|
---|
| 1493 | static mrb_value
|
---|
| 1494 | gc_step_ratio_set(mrb_state *mrb, mrb_value obj)
|
---|
| 1495 | {
|
---|
| 1496 | mrb_int ratio;
|
---|
| 1497 |
|
---|
| 1498 | mrb_get_args(mrb, "i", &ratio);
|
---|
[439] | 1499 | mrb->gc.step_ratio = (int)ratio;
|
---|
[270] | 1500 | return mrb_nil_value();
|
---|
| 1501 | }
|
---|
| 1502 |
|
---|
| 1503 | static void
|
---|
| 1504 | change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable)
|
---|
| 1505 | {
|
---|
[331] | 1506 | if (gc->disabled || gc->iterating) {
|
---|
| 1507 | mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled");
|
---|
| 1508 | return;
|
---|
| 1509 | }
|
---|
[270] | 1510 | if (is_generational(gc) && !enable) {
|
---|
| 1511 | clear_all_old(mrb, gc);
|
---|
| 1512 | mrb_assert(gc->state == MRB_GC_STATE_ROOT);
|
---|
| 1513 | gc->full = FALSE;
|
---|
| 1514 | }
|
---|
| 1515 | else if (!is_generational(gc) && enable) {
|
---|
| 1516 | incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
|
---|
[439] | 1517 | gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
|
---|
[270] | 1518 | gc->full = FALSE;
|
---|
| 1519 | }
|
---|
| 1520 | gc->generational = enable;
|
---|
| 1521 | }
|
---|
| 1522 |
|
---|
| 1523 | /*
|
---|
| 1524 | * call-seq:
|
---|
| 1525 | * GC.generational_mode -> true or false
|
---|
| 1526 | *
|
---|
| 1527 | * Returns generational or normal gc mode.
|
---|
| 1528 | *
|
---|
| 1529 | */
|
---|
| 1530 |
|
---|
| 1531 | static mrb_value
|
---|
| 1532 | gc_generational_mode_get(mrb_state *mrb, mrb_value self)
|
---|
| 1533 | {
|
---|
| 1534 | return mrb_bool_value(mrb->gc.generational);
|
---|
| 1535 | }
|
---|
| 1536 |
|
---|
| 1537 | /*
|
---|
| 1538 | * call-seq:
|
---|
| 1539 | * GC.generational_mode = true or false -> true or false
|
---|
| 1540 | *
|
---|
| 1541 | * Changes to generational or normal gc mode.
|
---|
| 1542 | *
|
---|
| 1543 | */
|
---|
| 1544 |
|
---|
| 1545 | static mrb_value
|
---|
| 1546 | gc_generational_mode_set(mrb_state *mrb, mrb_value self)
|
---|
| 1547 | {
|
---|
| 1548 | mrb_bool enable;
|
---|
| 1549 |
|
---|
| 1550 | mrb_get_args(mrb, "b", &enable);
|
---|
| 1551 | if (mrb->gc.generational != enable)
|
---|
| 1552 | change_gen_gc_mode(mrb, &mrb->gc, enable);
|
---|
| 1553 |
|
---|
| 1554 | return mrb_bool_value(enable);
|
---|
| 1555 | }
|
---|
| 1556 |
|
---|
| 1557 |
|
---|
| 1558 | static void
|
---|
| 1559 | gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data)
|
---|
| 1560 | {
|
---|
[331] | 1561 | mrb_heap_page* page;
|
---|
[270] | 1562 |
|
---|
[331] | 1563 | page = gc->heaps;
|
---|
[270] | 1564 | while (page != NULL) {
|
---|
[331] | 1565 | RVALUE *p;
|
---|
| 1566 | int i;
|
---|
[270] | 1567 |
|
---|
| 1568 | p = objects(page);
|
---|
[331] | 1569 | for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) {
|
---|
| 1570 | if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK)
|
---|
| 1571 | return;
|
---|
[270] | 1572 | }
|
---|
| 1573 | page = page->next;
|
---|
| 1574 | }
|
---|
| 1575 | }
|
---|
| 1576 |
|
---|
| 1577 | void
|
---|
| 1578 | mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
|
---|
| 1579 | {
|
---|
[331] | 1580 | mrb_bool iterating = mrb->gc.iterating;
|
---|
| 1581 |
|
---|
[439] | 1582 | mrb_full_gc(mrb);
|
---|
[331] | 1583 | mrb->gc.iterating = TRUE;
|
---|
| 1584 | if (iterating) {
|
---|
| 1585 | gc_each_objects(mrb, &mrb->gc, callback, data);
|
---|
| 1586 | }
|
---|
| 1587 | else {
|
---|
| 1588 | struct mrb_jmpbuf *prev_jmp = mrb->jmp;
|
---|
| 1589 | struct mrb_jmpbuf c_jmp;
|
---|
| 1590 |
|
---|
| 1591 | MRB_TRY(&c_jmp) {
|
---|
| 1592 | mrb->jmp = &c_jmp;
|
---|
| 1593 | gc_each_objects(mrb, &mrb->gc, callback, data);
|
---|
| 1594 | mrb->jmp = prev_jmp;
|
---|
[439] | 1595 | mrb->gc.iterating = iterating;
|
---|
[331] | 1596 | } MRB_CATCH(&c_jmp) {
|
---|
| 1597 | mrb->gc.iterating = iterating;
|
---|
| 1598 | mrb->jmp = prev_jmp;
|
---|
| 1599 | MRB_THROW(prev_jmp);
|
---|
| 1600 | } MRB_END_EXC(&c_jmp);
|
---|
| 1601 | }
|
---|
[270] | 1602 | }
|
---|
| 1603 |
|
---|
| 1604 | #ifdef GC_TEST
|
---|
| 1605 | #ifdef GC_DEBUG
|
---|
| 1606 | static mrb_value gc_test(mrb_state *, mrb_value);
|
---|
| 1607 | #endif
|
---|
| 1608 | #endif
|
---|
| 1609 |
|
---|
| 1610 | void
|
---|
| 1611 | mrb_init_gc(mrb_state *mrb)
|
---|
| 1612 | {
|
---|
| 1613 | struct RClass *gc;
|
---|
| 1614 |
|
---|
[439] | 1615 | mrb_static_assert(sizeof(RVALUE) <= sizeof(void*) * 6,
|
---|
| 1616 | "RVALUE size must be within 6 words");
|
---|
| 1617 |
|
---|
[270] | 1618 | gc = mrb_define_module(mrb, "GC");
|
---|
| 1619 |
|
---|
| 1620 | mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE());
|
---|
| 1621 | mrb_define_class_method(mrb, gc, "enable", gc_enable, MRB_ARGS_NONE());
|
---|
| 1622 | mrb_define_class_method(mrb, gc, "disable", gc_disable, MRB_ARGS_NONE());
|
---|
| 1623 | mrb_define_class_method(mrb, gc, "interval_ratio", gc_interval_ratio_get, MRB_ARGS_NONE());
|
---|
| 1624 | mrb_define_class_method(mrb, gc, "interval_ratio=", gc_interval_ratio_set, MRB_ARGS_REQ(1));
|
---|
| 1625 | mrb_define_class_method(mrb, gc, "step_ratio", gc_step_ratio_get, MRB_ARGS_NONE());
|
---|
| 1626 | mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1));
|
---|
| 1627 | mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1));
|
---|
| 1628 | mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE());
|
---|
| 1629 | #ifdef GC_TEST
|
---|
| 1630 | #ifdef GC_DEBUG
|
---|
| 1631 | mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE());
|
---|
| 1632 | #endif
|
---|
| 1633 | #endif
|
---|
| 1634 | }
|
---|
| 1635 |
|
---|
| 1636 | #ifdef GC_TEST
|
---|
| 1637 | #ifdef GC_DEBUG
|
---|
| 1638 | void
|
---|
| 1639 | test_mrb_field_write_barrier(void)
|
---|
| 1640 | {
|
---|
| 1641 | mrb_state *mrb = mrb_open();
|
---|
| 1642 | struct RBasic *obj, *value;
|
---|
| 1643 | mrb_gc *gc = &mrb->gc;
|
---|
| 1644 |
|
---|
| 1645 | puts("test_mrb_field_write_barrier");
|
---|
| 1646 | gc->generational = FALSE;
|
---|
| 1647 | obj = mrb_basic_ptr(mrb_ary_new(mrb));
|
---|
| 1648 | value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value"));
|
---|
| 1649 | paint_black(obj);
|
---|
| 1650 | paint_partial_white(gc, value);
|
---|
| 1651 |
|
---|
| 1652 |
|
---|
| 1653 | puts(" in MRB_GC_STATE_MARK");
|
---|
| 1654 | gc->state = MRB_GC_STATE_MARK;
|
---|
| 1655 | mrb_field_write_barrier(mrb, obj, value);
|
---|
| 1656 |
|
---|
| 1657 | mrb_assert(is_gray(value));
|
---|
| 1658 |
|
---|
| 1659 |
|
---|
| 1660 | puts(" in MRB_GC_STATE_SWEEP");
|
---|
| 1661 | paint_partial_white(gc, value);
|
---|
| 1662 | gc->state = MRB_GC_STATE_SWEEP;
|
---|
| 1663 | mrb_field_write_barrier(mrb, obj, value);
|
---|
| 1664 |
|
---|
| 1665 | mrb_assert(obj->color & gc->current_white_part);
|
---|
| 1666 | mrb_assert(value->color & gc->current_white_part);
|
---|
| 1667 |
|
---|
| 1668 |
|
---|
| 1669 | puts(" fail with black");
|
---|
| 1670 | gc->state = MRB_GC_STATE_MARK;
|
---|
| 1671 | paint_white(obj);
|
---|
| 1672 | paint_partial_white(gc, value);
|
---|
| 1673 | mrb_field_write_barrier(mrb, obj, value);
|
---|
| 1674 |
|
---|
| 1675 | mrb_assert(obj->color & gc->current_white_part);
|
---|
| 1676 |
|
---|
| 1677 |
|
---|
| 1678 | puts(" fail with gray");
|
---|
| 1679 | gc->state = MRB_GC_STATE_MARK;
|
---|
| 1680 | paint_black(obj);
|
---|
| 1681 | paint_gray(value);
|
---|
| 1682 | mrb_field_write_barrier(mrb, obj, value);
|
---|
| 1683 |
|
---|
| 1684 | mrb_assert(is_gray(value));
|
---|
| 1685 |
|
---|
| 1686 |
|
---|
| 1687 | {
|
---|
| 1688 | puts("test_mrb_field_write_barrier_value");
|
---|
| 1689 | obj = mrb_basic_ptr(mrb_ary_new(mrb));
|
---|
| 1690 | mrb_value value = mrb_str_new_lit(mrb, "value");
|
---|
| 1691 | paint_black(obj);
|
---|
| 1692 | paint_partial_white(gc, mrb_basic_ptr(value));
|
---|
| 1693 |
|
---|
| 1694 | gc->state = MRB_GC_STATE_MARK;
|
---|
| 1695 | mrb_field_write_barrier_value(mrb, obj, value);
|
---|
| 1696 |
|
---|
| 1697 | mrb_assert(is_gray(mrb_basic_ptr(value)));
|
---|
| 1698 | }
|
---|
| 1699 |
|
---|
| 1700 | mrb_close(mrb);
|
---|
| 1701 | }
|
---|
| 1702 |
|
---|
| 1703 | void
|
---|
| 1704 | test_mrb_write_barrier(void)
|
---|
| 1705 | {
|
---|
| 1706 | mrb_state *mrb = mrb_open();
|
---|
| 1707 | struct RBasic *obj;
|
---|
| 1708 | mrb_gc *gc = &mrb->gc;
|
---|
| 1709 |
|
---|
| 1710 | puts("test_mrb_write_barrier");
|
---|
| 1711 | obj = mrb_basic_ptr(mrb_ary_new(mrb));
|
---|
| 1712 | paint_black(obj);
|
---|
| 1713 |
|
---|
| 1714 | puts(" in MRB_GC_STATE_MARK");
|
---|
| 1715 | gc->state = MRB_GC_STATE_MARK;
|
---|
| 1716 | mrb_write_barrier(mrb, obj);
|
---|
| 1717 |
|
---|
| 1718 | mrb_assert(is_gray(obj));
|
---|
| 1719 | mrb_assert(gc->atomic_gray_list == obj);
|
---|
| 1720 |
|
---|
| 1721 |
|
---|
| 1722 | puts(" fail with gray");
|
---|
| 1723 | paint_gray(obj);
|
---|
| 1724 | mrb_write_barrier(mrb, obj);
|
---|
| 1725 |
|
---|
| 1726 | mrb_assert(is_gray(obj));
|
---|
| 1727 |
|
---|
| 1728 | mrb_close(mrb);
|
---|
| 1729 | }
|
---|
| 1730 |
|
---|
| 1731 | void
|
---|
| 1732 | test_add_gray_list(void)
|
---|
| 1733 | {
|
---|
| 1734 | mrb_state *mrb = mrb_open();
|
---|
| 1735 | struct RBasic *obj1, *obj2;
|
---|
| 1736 | mrb_gc *gc = &mrb->gc;
|
---|
| 1737 |
|
---|
| 1738 | puts("test_add_gray_list");
|
---|
| 1739 | change_gen_gc_mode(mrb, gc, FALSE);
|
---|
| 1740 | mrb_assert(gc->gray_list == NULL);
|
---|
| 1741 | obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
|
---|
| 1742 | add_gray_list(mrb, gc, obj1);
|
---|
| 1743 | mrb_assert(gc->gray_list == obj1);
|
---|
| 1744 | mrb_assert(is_gray(obj1));
|
---|
| 1745 |
|
---|
| 1746 | obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
|
---|
| 1747 | add_gray_list(mrb, gc, obj2);
|
---|
| 1748 | mrb_assert(gc->gray_list == obj2);
|
---|
| 1749 | mrb_assert(gc->gray_list->gcnext == obj1);
|
---|
| 1750 | mrb_assert(is_gray(obj2));
|
---|
| 1751 |
|
---|
| 1752 | mrb_close(mrb);
|
---|
| 1753 | }
|
---|
| 1754 |
|
---|
| 1755 | void
|
---|
| 1756 | test_gc_gray_mark(void)
|
---|
| 1757 | {
|
---|
| 1758 | mrb_state *mrb = mrb_open();
|
---|
| 1759 | mrb_value obj_v, value_v;
|
---|
| 1760 | struct RBasic *obj;
|
---|
| 1761 | size_t gray_num = 0;
|
---|
| 1762 | mrb_gc *gc = &mrb->gc;
|
---|
| 1763 |
|
---|
| 1764 | puts("test_gc_gray_mark");
|
---|
| 1765 |
|
---|
| 1766 | puts(" in MRB_TT_CLASS");
|
---|
| 1767 | obj = (struct RBasic*)mrb->object_class;
|
---|
| 1768 | paint_gray(obj);
|
---|
| 1769 | gray_num = gc_gray_mark(mrb, gc, obj);
|
---|
| 1770 | mrb_assert(is_black(obj));
|
---|
| 1771 | mrb_assert(gray_num > 1);
|
---|
| 1772 |
|
---|
| 1773 | puts(" in MRB_TT_ARRAY");
|
---|
| 1774 | obj_v = mrb_ary_new(mrb);
|
---|
| 1775 | value_v = mrb_str_new_lit(mrb, "test");
|
---|
| 1776 | paint_gray(mrb_basic_ptr(obj_v));
|
---|
| 1777 | paint_partial_white(gc, mrb_basic_ptr(value_v));
|
---|
| 1778 | mrb_ary_push(mrb, obj_v, value_v);
|
---|
| 1779 | gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v));
|
---|
| 1780 | mrb_assert(is_black(mrb_basic_ptr(obj_v)));
|
---|
| 1781 | mrb_assert(is_gray(mrb_basic_ptr(value_v)));
|
---|
| 1782 | mrb_assert(gray_num == 1);
|
---|
| 1783 |
|
---|
| 1784 | mrb_close(mrb);
|
---|
| 1785 | }
|
---|
| 1786 |
|
---|
| 1787 | void
|
---|
| 1788 | test_incremental_gc(void)
|
---|
| 1789 | {
|
---|
| 1790 | mrb_state *mrb = mrb_open();
|
---|
| 1791 | size_t max = ~0, live = 0, total = 0, freed = 0;
|
---|
| 1792 | RVALUE *free;
|
---|
| 1793 | mrb_heap_page *page;
|
---|
| 1794 | mrb_gc *gc = &mrb->gc;
|
---|
| 1795 |
|
---|
| 1796 | puts("test_incremental_gc");
|
---|
| 1797 | change_gen_gc_mode(mrb, gc, FALSE);
|
---|
| 1798 |
|
---|
| 1799 | puts(" in mrb_full_gc");
|
---|
| 1800 | mrb_full_gc(mrb);
|
---|
| 1801 |
|
---|
| 1802 | mrb_assert(gc->state == MRB_GC_STATE_ROOT);
|
---|
| 1803 | puts(" in MRB_GC_STATE_ROOT");
|
---|
| 1804 | incremental_gc(mrb, gc, max);
|
---|
| 1805 | mrb_assert(gc->state == MRB_GC_STATE_MARK);
|
---|
| 1806 | puts(" in MRB_GC_STATE_MARK");
|
---|
| 1807 | incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP);
|
---|
| 1808 | mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
|
---|
| 1809 |
|
---|
| 1810 | puts(" in MRB_GC_STATE_SWEEP");
|
---|
| 1811 | page = gc->heaps;
|
---|
| 1812 | while (page) {
|
---|
| 1813 | RVALUE *p = objects(page);
|
---|
| 1814 | RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
|
---|
| 1815 | while (p<e) {
|
---|
| 1816 | if (is_black(&p->as.basic)) {
|
---|
| 1817 | live++;
|
---|
| 1818 | }
|
---|
| 1819 | if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) {
|
---|
| 1820 | printf("%p\n", &p->as.basic);
|
---|
| 1821 | }
|
---|
| 1822 | p++;
|
---|
| 1823 | }
|
---|
| 1824 | page = page->next;
|
---|
| 1825 | total += MRB_HEAP_PAGE_SIZE;
|
---|
| 1826 | }
|
---|
| 1827 |
|
---|
| 1828 | mrb_assert(gc->gray_list == NULL);
|
---|
| 1829 |
|
---|
| 1830 | incremental_gc(mrb, gc, max);
|
---|
| 1831 | mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
|
---|
| 1832 |
|
---|
| 1833 | incremental_gc(mrb, gc, max);
|
---|
| 1834 | mrb_assert(gc->state == MRB_GC_STATE_ROOT);
|
---|
| 1835 |
|
---|
| 1836 | free = (RVALUE*)gc->heaps->freelist;
|
---|
| 1837 | while (free) {
|
---|
| 1838 | freed++;
|
---|
| 1839 | free = (RVALUE*)free->as.free.next;
|
---|
| 1840 | }
|
---|
| 1841 |
|
---|
| 1842 | mrb_assert(gc->live == live);
|
---|
| 1843 | mrb_assert(gc->live == total-freed);
|
---|
| 1844 |
|
---|
| 1845 | puts("test_incremental_gc(gen)");
|
---|
| 1846 | incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP);
|
---|
| 1847 | change_gen_gc_mode(mrb, gc, TRUE);
|
---|
| 1848 |
|
---|
| 1849 | mrb_assert(gc->full == FALSE);
|
---|
| 1850 | mrb_assert(gc->state == MRB_GC_STATE_ROOT);
|
---|
| 1851 |
|
---|
| 1852 | puts(" in minor");
|
---|
| 1853 | mrb_assert(is_minor_gc(gc));
|
---|
| 1854 | mrb_assert(gc->majorgc_old_threshold > 0);
|
---|
| 1855 | gc->majorgc_old_threshold = 0;
|
---|
| 1856 | mrb_incremental_gc(mrb);
|
---|
| 1857 | mrb_assert(gc->full == TRUE);
|
---|
| 1858 | mrb_assert(gc->state == MRB_GC_STATE_ROOT);
|
---|
| 1859 |
|
---|
| 1860 | puts(" in major");
|
---|
| 1861 | mrb_assert(is_major_gc(gc));
|
---|
| 1862 | do {
|
---|
| 1863 | mrb_incremental_gc(mrb);
|
---|
| 1864 | } while (gc->state != MRB_GC_STATE_ROOT);
|
---|
| 1865 | mrb_assert(gc->full == FALSE);
|
---|
| 1866 |
|
---|
| 1867 | mrb_close(mrb);
|
---|
| 1868 | }
|
---|
| 1869 |
|
---|
| 1870 | void
|
---|
| 1871 | test_incremental_sweep_phase(void)
|
---|
| 1872 | {
|
---|
| 1873 | mrb_state *mrb = mrb_open();
|
---|
| 1874 | mrb_gc *gc = &mrb->gc;
|
---|
| 1875 |
|
---|
| 1876 | puts("test_incremental_sweep_phase");
|
---|
| 1877 |
|
---|
| 1878 | add_heap(mrb, gc);
|
---|
| 1879 | gc->sweeps = gc->heaps;
|
---|
| 1880 |
|
---|
| 1881 | mrb_assert(gc->heaps->next->next == NULL);
|
---|
| 1882 | mrb_assert(gc->free_heaps->next->next == NULL);
|
---|
| 1883 | incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3);
|
---|
| 1884 |
|
---|
| 1885 | mrb_assert(gc->heaps->next == NULL);
|
---|
| 1886 | mrb_assert(gc->heaps == gc->free_heaps);
|
---|
| 1887 |
|
---|
| 1888 | mrb_close(mrb);
|
---|
| 1889 | }
|
---|
| 1890 |
|
---|
| 1891 | static mrb_value
|
---|
| 1892 | gc_test(mrb_state *mrb, mrb_value self)
|
---|
| 1893 | {
|
---|
| 1894 | test_mrb_field_write_barrier();
|
---|
| 1895 | test_mrb_write_barrier();
|
---|
| 1896 | test_add_gray_list();
|
---|
| 1897 | test_gc_gray_mark();
|
---|
| 1898 | test_incremental_gc();
|
---|
| 1899 | test_incremental_sweep_phase();
|
---|
| 1900 | return mrb_nil_value();
|
---|
| 1901 | }
|
---|
| 1902 | #endif /* GC_DEBUG */
|
---|
| 1903 | #endif /* GC_TEST */
|
---|