1 /**********************************************************************
3 transient_heap.c - implement transient_heap.
5 Copyright (C) 2018 Koichi Sasada
7 **********************************************************************/
9 #include "debug_counter.h"
12 #include "internal/gc.h"
13 #include "internal/hash.h"
14 #include "internal/sanitizers.h"
15 #include "internal/static_assert.h"
16 #include "internal/struct.h"
17 #include "internal/variable.h"
18 #include "ruby/debug.h"
19 #include "ruby/ruby.h"
20 #include "ruby_assert.h"
21 #include "transient_heap.h"
25 #if USE_TRANSIENT_HEAP /* USE_TRANSIENT_HEAP */
27 * 1: enable assertions
28 * 2: enable verify all transient heaps
30 #ifndef TRANSIENT_HEAP_CHECK_MODE
31 #define TRANSIENT_HEAP_CHECK_MODE 0
33 #define TH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(TRANSIENT_HEAP_CHECK_MODE > 0, expr, #expr)
37 * 2: show dump at events
38 * 3: show all operations
40 #define TRANSIENT_HEAP_DEBUG 0
42 /* For Debug: Provide blocks infinitely.
43 * This mode generates blocks unlimitedly
44 * and prohibit access free'ed blocks to check invalid access.
46 #define TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK 0
48 #if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
53 /* For Debug: Prohibit promoting to malloc space.
55 #define TRANSIENT_HEAP_DEBUG_DONT_PROMOTE 0
57 /* size configuration */
58 #define TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE 1024
61 #define TRANSIENT_HEAP_BLOCK_SIZE (1024 * 32 ) /* 32KB int16_t */
62 #ifndef TRANSIENT_HEAP_TOTAL_SIZE
63 #define TRANSIENT_HEAP_TOTAL_SIZE (1024 * 1024 * 32) /* 32 MB */
65 #define TRANSIENT_HEAP_ALLOC_MAX (1024 * 2 ) /* 2 KB */
66 #define TRANSIENT_HEAP_BLOCK_NUM (TRANSIENT_HEAP_TOTAL_SIZE / TRANSIENT_HEAP_BLOCK_SIZE)
67 #define TRANSIENT_HEAP_USABLE_SIZE (TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header))
69 #define TRANSIENT_HEAP_ALLOC_MAGIC 0xfeab
70 #define TRANSIENT_HEAP_ALLOC_ALIGN RUBY_ALIGNOF(void *)
72 #define TRANSIENT_HEAP_ALLOC_MARKING_LAST -1
73 #define TRANSIENT_HEAP_ALLOC_MARKING_FREE -2
75 enum transient_heap_status
{
77 transient_heap_marking
,
78 transient_heap_escaping
81 struct transient_heap_block
{
82 struct transient_heap_block_header
{
84 int16_t last_marked_index
;
86 struct transient_heap_block
*next_block
;
88 char buff
[TRANSIENT_HEAP_USABLE_SIZE
];
91 struct transient_heap
{
92 struct transient_heap_block
*using_blocks
;
93 struct transient_heap_block
*marked_blocks
;
94 struct transient_heap_block
*free_blocks
;
96 int total_marked_objects
;
98 enum transient_heap_status status
;
100 VALUE
*promoted_objects
;
101 int promoted_objects_size
;
102 int promoted_objects_index
;
104 struct transient_heap_block
*arena
;
105 int arena_index
; /* increment only */
108 struct transient_alloc_header
{
111 int16_t next_marked_index
;
116 static struct transient_heap global_transient_heap
;
118 static void transient_heap_promote_add(struct transient_heap
* theap
, VALUE obj
);
119 static const void *transient_heap_ptr(VALUE obj
, int error
);
120 static int transient_header_managed_ptr_p(struct transient_heap
* theap
, const void *ptr
);
122 #define ROUND_UP(v, a) (((size_t)(v) + (a) - 1) & ~((a) - 1))
125 transient_heap_block_dump(struct transient_heap
* theap
, struct transient_heap_block
*block
)
129 while (i
<block
->info
.index
) {
130 void *ptr
= &block
->buff
[i
];
131 struct transient_alloc_header
*header
= ptr
;
132 fprintf(stderr
, "%4d %8d %p size:%4d next:%4d %s\n", n
, i
, ptr
, header
->size
, header
->next_marked_index
, rb_obj_info(header
->obj
));
139 transient_heap_blocks_dump(struct transient_heap
* theap
, struct transient_heap_block
*block
, const char *type_str
)
142 fprintf(stderr
, "- transient_heap_dump: %s:%p index:%d objects:%d last_marked_index:%d next:%p\n",
143 type_str
, (void *)block
, block
->info
.index
, block
->info
.objects
, block
->info
.last_marked_index
, (void *)block
->info
.next_block
);
145 transient_heap_block_dump(theap
, block
);
146 block
= block
->info
.next_block
;
151 transient_heap_dump(struct transient_heap
* theap
)
153 fprintf(stderr
, "transient_heap_dump objects:%d marked_objects:%d blocks:%d\n", theap
->total_objects
, theap
->total_marked_objects
, theap
->total_blocks
);
154 transient_heap_blocks_dump(theap
, theap
->using_blocks
, "using_blocks");
155 transient_heap_blocks_dump(theap
, theap
->marked_blocks
, "marked_blocks");
156 transient_heap_blocks_dump(theap
, theap
->free_blocks
, "free_blocks");
159 /* Debug: dump all transient_heap blocks */
161 rb_transient_heap_dump(void)
163 transient_heap_dump(&global_transient_heap
);
166 #if TRANSIENT_HEAP_CHECK_MODE >= 2
167 ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static void transient_heap_ptr_check(struct transient_heap
*theap
, VALUE obj
));
169 transient_heap_ptr_check(struct transient_heap
*theap
, VALUE obj
)
172 const void *ptr
= transient_heap_ptr(obj
, FALSE
);
173 TH_ASSERT(ptr
== NULL
|| transient_header_managed_ptr_p(theap
, ptr
));
177 ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static int transient_heap_block_verify(struct transient_heap
*theap
, struct transient_heap_block
*block
));
179 transient_heap_block_verify(struct transient_heap
*theap
, struct transient_heap_block
*block
)
182 struct transient_alloc_header
*header
;
184 while (i
<block
->info
.index
) {
185 header
= (void *)&block
->buff
[i
];
186 TH_ASSERT(header
->magic
== TRANSIENT_HEAP_ALLOC_MAGIC
);
187 transient_heap_ptr_check(theap
, header
->obj
);
191 TH_ASSERT(block
->info
.objects
== n
);
197 transient_heap_blocks_verify(struct transient_heap
*theap
, struct transient_heap_block
*blocks
, int *block_num_ptr
)
200 struct transient_heap_block
*block
= blocks
;
202 n
+= transient_heap_block_verify(theap
, block
);
204 block
= block
->info
.next_block
;
212 transient_heap_verify(struct transient_heap
*theap
)
214 #if TRANSIENT_HEAP_CHECK_MODE >= 2
215 int n
=0, block_num
=0;
217 n
+= transient_heap_blocks_verify(theap
, theap
->using_blocks
, &block_num
);
218 n
+= transient_heap_blocks_verify(theap
, theap
->marked_blocks
, &block_num
);
220 TH_ASSERT(n
== theap
->total_objects
);
221 TH_ASSERT(n
>= theap
->total_marked_objects
);
222 TH_ASSERT(block_num
== theap
->total_blocks
);
226 /* Debug: check assertions for all transient_heap blocks */
228 rb_transient_heap_verify(void)
230 transient_heap_verify(&global_transient_heap
);
233 static struct transient_heap
*
234 transient_heap_get(void)
236 struct transient_heap
* theap
= &global_transient_heap
;
237 transient_heap_verify(theap
);
242 reset_block(struct transient_heap_block
*block
)
244 __msan_allocated_memory(block
, sizeof block
);
245 block
->info
.index
= 0;
246 block
->info
.objects
= 0;
247 block
->info
.last_marked_index
= TRANSIENT_HEAP_ALLOC_MARKING_LAST
;
248 block
->info
.next_block
= NULL
;
249 __asan_poison_memory_region(&block
->buff
, sizeof block
->buff
);
253 connect_to_free_blocks(struct transient_heap
*theap
, struct transient_heap_block
*block
)
255 block
->info
.next_block
= theap
->free_blocks
;
256 theap
->free_blocks
= block
;
260 connect_to_using_blocks(struct transient_heap
*theap
, struct transient_heap_block
*block
)
262 block
->info
.next_block
= theap
->using_blocks
;
263 theap
->using_blocks
= block
;
268 connect_to_marked_blocks(struct transient_heap
*theap
, struct transient_heap_block
*block
)
270 block
->info
.next_block
= theap
->marked_blocks
;
271 theap
->marked_blocks
= block
;
276 append_to_marked_blocks(struct transient_heap
*theap
, struct transient_heap_block
*append_blocks
)
278 if (theap
->marked_blocks
) {
279 struct transient_heap_block
*block
= theap
->marked_blocks
, *last_block
= NULL
;
282 block
= block
->info
.next_block
;
285 TH_ASSERT(last_block
->info
.next_block
== NULL
);
286 last_block
->info
.next_block
= append_blocks
;
289 theap
->marked_blocks
= append_blocks
;
293 static struct transient_heap_block
*
294 transient_heap_block_alloc(struct transient_heap
* theap
)
296 struct transient_heap_block
*block
;
297 #if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
298 block
= mmap(NULL
, TRANSIENT_HEAP_BLOCK_SIZE
, PROT_READ
| PROT_WRITE
,
299 MAP_PRIVATE
| MAP_ANONYMOUS
,
301 if (block
== MAP_FAILED
) rb_bug("transient_heap_block_alloc: err:%d\n", errno
);
303 if (theap
->arena
== NULL
) {
304 theap
->arena
= rb_aligned_malloc(TRANSIENT_HEAP_BLOCK_SIZE
, TRANSIENT_HEAP_TOTAL_SIZE
);
305 if (theap
->arena
== NULL
) {
306 rb_bug("transient_heap_block_alloc: failed\n");
310 TH_ASSERT(theap
->arena_index
< TRANSIENT_HEAP_BLOCK_NUM
);
311 block
= &theap
->arena
[theap
->arena_index
++];
312 TH_ASSERT(((intptr_t)block
& (TRANSIENT_HEAP_BLOCK_SIZE
- 1)) == 0);
316 TH_ASSERT(((intptr_t)block
->buff
& (TRANSIENT_HEAP_ALLOC_ALIGN
-1)) == 0);
317 if (0) fprintf(stderr
, "transient_heap_block_alloc: %4d %p\n", theap
->total_blocks
, (void *)block
);
322 static struct transient_heap_block
*
323 transient_heap_allocatable_block(struct transient_heap
* theap
)
325 struct transient_heap_block
*block
;
327 #if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
328 block
= transient_heap_block_alloc(theap
);
329 theap
->total_blocks
++;
331 /* get one block from free_blocks */
332 block
= theap
->free_blocks
;
334 theap
->free_blocks
= block
->info
.next_block
;
335 block
->info
.next_block
= NULL
;
336 theap
->total_blocks
++;
343 static struct transient_alloc_header
*
344 transient_heap_allocatable_header(struct transient_heap
* theap
, size_t size
)
346 struct transient_heap_block
*block
= theap
->using_blocks
;
349 TH_ASSERT(block
->info
.index
<= (int16_t)TRANSIENT_HEAP_USABLE_SIZE
);
351 if (TRANSIENT_HEAP_USABLE_SIZE
- block
->info
.index
>= size
) {
352 struct transient_alloc_header
*header
= (void *)&block
->buff
[block
->info
.index
];
353 block
->info
.index
+= size
;
354 block
->info
.objects
++;
358 block
= transient_heap_allocatable_block(theap
);
359 if (block
) connect_to_using_blocks(theap
, block
);
367 rb_transient_heap_alloc(VALUE obj
, size_t req_size
)
369 // only on single main ractor
370 if (ruby_single_main_ractor
== NULL
) return NULL
;
373 struct transient_heap
* theap
= transient_heap_get();
374 size_t size
= ROUND_UP(req_size
+ sizeof(struct transient_alloc_header
), TRANSIENT_HEAP_ALLOC_ALIGN
);
376 TH_ASSERT(RB_TYPE_P(obj
, T_ARRAY
) ||
377 RB_TYPE_P(obj
, T_OBJECT
) ||
378 RB_TYPE_P(obj
, T_STRUCT
) ||
379 RB_TYPE_P(obj
, T_HASH
)); /* supported types */
381 if (size
> TRANSIENT_HEAP_ALLOC_MAX
) {
382 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_alloc: [too big: %ld] %s\n", (long)size
, rb_obj_info(obj
));
385 #if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE == 0
386 else if (RB_OBJ_PROMOTED_RAW(obj
)) {
387 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_alloc: [promoted object] %s\n", rb_obj_info(obj
));
391 else if (RBASIC_CLASS(obj
) == 0) {
392 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_alloc: [hidden object] %s\n", rb_obj_info(obj
));
397 struct transient_alloc_header
*header
= transient_heap_allocatable_header(theap
, size
);
401 /* header is poisoned to prevent buffer overflow, should
402 * unpoison first... */
403 asan_unpoison_memory_region(header
, sizeof *header
, true);
406 header
->magic
= TRANSIENT_HEAP_ALLOC_MAGIC
;
407 header
->next_marked_index
= TRANSIENT_HEAP_ALLOC_MARKING_FREE
;
408 header
->obj
= obj
; /* TODO: can we eliminate it? */
410 /* header is fixed; shall poison again */
411 asan_poison_memory_region(header
, sizeof *header
);
414 theap
->total_objects
++; /* statistics */
416 #if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
417 if (RB_OBJ_PROMOTED_RAW(obj
)) {
418 transient_heap_promote_add(theap
, obj
);
421 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_alloc: header:%p ptr:%p size:%d obj:%s\n", (void *)header
, ptr
, (int)size
, rb_obj_info(obj
));
423 RB_DEBUG_COUNTER_INC(theap_alloc
);
425 /* ptr is set up; OK to unpoison. */
426 asan_unpoison_memory_region(ptr
, size
- sizeof *header
, true);
430 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_alloc: [no enough space: %ld] %s\n", (long)size
, rb_obj_info(obj
));
431 RB_DEBUG_COUNTER_INC(theap_alloc_fail
);
440 Init_TransientHeap(void)
443 struct transient_heap
* theap
= transient_heap_get();
445 #if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
448 TH_ASSERT(TRANSIENT_HEAP_BLOCK_SIZE
* TRANSIENT_HEAP_BLOCK_NUM
== TRANSIENT_HEAP_TOTAL_SIZE
);
449 block_num
= TRANSIENT_HEAP_BLOCK_NUM
;
451 for (i
=0; i
<block_num
; i
++) {
452 connect_to_free_blocks(theap
, transient_heap_block_alloc(theap
));
454 theap
->using_blocks
= transient_heap_allocatable_block(theap
);
456 theap
->promoted_objects_size
= TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE
;
457 theap
->promoted_objects_index
= 0;
458 /* should not use ALLOC_N to be free from GC */
459 theap
->promoted_objects
= malloc(sizeof(VALUE
) * theap
->promoted_objects_size
);
462 sizeof(VALUE
) <= SIZE_MAX
/ TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE
);
463 if (theap
->promoted_objects
== NULL
) rb_bug("Init_TransientHeap: malloc failed.");
466 static struct transient_heap_block
*
467 blocks_alloc_header_to_block(struct transient_heap
*theap
, struct transient_heap_block
*blocks
, struct transient_alloc_header
*header
)
469 struct transient_heap_block
*block
= blocks
;
472 if (block
->buff
<= (char *)header
&& (char *)header
< block
->buff
+ TRANSIENT_HEAP_USABLE_SIZE
) {
475 block
= block
->info
.next_block
;
481 static struct transient_heap_block
*
482 alloc_header_to_block_verbose(struct transient_heap
*theap
, struct transient_alloc_header
*header
)
484 struct transient_heap_block
*block
;
486 if ((block
= blocks_alloc_header_to_block(theap
, theap
->marked_blocks
, header
)) != NULL
) {
487 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "alloc_header_to_block: found in marked_blocks\n");
490 else if ((block
= blocks_alloc_header_to_block(theap
, theap
->using_blocks
, header
)) != NULL
) {
491 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "alloc_header_to_block: found in using_blocks\n");
499 static struct transient_alloc_header
*
500 ptr_to_alloc_header(const void *ptr
)
502 struct transient_alloc_header
*header
= (void *)ptr
;
508 transient_header_managed_ptr_p(struct transient_heap
* theap
, const void *ptr
)
510 if (alloc_header_to_block_verbose(theap
, ptr_to_alloc_header(ptr
))) {
520 rb_transient_heap_managed_ptr_p(const void *ptr
)
522 return transient_header_managed_ptr_p(transient_heap_get(), ptr
);
525 static struct transient_heap_block
*
526 alloc_header_to_block(struct transient_heap
*theap
, struct transient_alloc_header
*header
)
528 struct transient_heap_block
*block
;
529 #if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
530 block
= alloc_header_to_block_verbose(theap
, header
);
532 transient_heap_dump(theap
);
533 rb_bug("alloc_header_to_block: not found in mark_blocks (%p)\n", header
);
536 block
= (void *)((intptr_t)header
& ~(TRANSIENT_HEAP_BLOCK_SIZE
-1));
537 TH_ASSERT(block
== alloc_header_to_block_verbose(theap
, header
));
543 rb_transient_heap_mark(VALUE obj
, const void *ptr
)
547 struct transient_alloc_header
*header
= ptr_to_alloc_header(ptr
);
548 asan_unpoison_memory_region(header
, sizeof *header
, false);
549 if (header
->magic
!= TRANSIENT_HEAP_ALLOC_MAGIC
) rb_bug("rb_transient_heap_mark: wrong header, %s (%p)", rb_obj_info(obj
), ptr
);
550 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_mark: %s (%p)\n", rb_obj_info(obj
), ptr
);
552 #if TRANSIENT_HEAP_CHECK_MODE > 0
554 struct transient_heap
* theap
= transient_heap_get();
555 TH_ASSERT(theap
->status
== transient_heap_marking
);
556 TH_ASSERT(transient_header_managed_ptr_p(theap
, ptr
));
558 if (header
->magic
!= TRANSIENT_HEAP_ALLOC_MAGIC
) {
559 transient_heap_dump(theap
);
560 rb_bug("rb_transient_heap_mark: magic is broken");
562 else if (header
->obj
!= obj
) {
563 // transient_heap_dump(theap);
564 rb_bug("rb_transient_heap_mark: unmatch (%s is stored, but %s is given)\n",
565 rb_obj_info(header
->obj
), rb_obj_info(obj
));
570 if (header
->next_marked_index
!= TRANSIENT_HEAP_ALLOC_MARKING_FREE
) {
575 struct transient_heap
* theap
= transient_heap_get();
576 struct transient_heap_block
*block
= alloc_header_to_block(theap
, header
);
577 __asan_unpoison_memory_region(&block
->info
, sizeof block
->info
);
578 header
->next_marked_index
= block
->info
.last_marked_index
;
579 block
->info
.last_marked_index
= (int)((char *)header
- block
->buff
);
580 theap
->total_marked_objects
++;
582 transient_heap_verify(theap
);
586 ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static const void *transient_heap_ptr(VALUE obj
, int error
));
588 transient_heap_ptr(VALUE obj
, int error
)
590 const void *ptr
= NULL
;
592 switch (BUILTIN_TYPE(obj
)) {
594 if (RARRAY_TRANSIENT_P(obj
)) {
595 TH_ASSERT(!FL_TEST_RAW(obj
, RARRAY_EMBED_FLAG
));
596 ptr
= RARRAY(obj
)->as
.heap
.ptr
;
600 if (ROBJ_TRANSIENT_P(obj
)) {
601 ptr
= ROBJECT_IVPTR(obj
);
605 if (RSTRUCT_TRANSIENT_P(obj
)) {
606 ptr
= rb_struct_const_heap_ptr(obj
);
610 if (RHASH_TRANSIENT_P(obj
)) {
611 TH_ASSERT(RHASH_AR_TABLE_P(obj
));
612 ptr
= (VALUE
*)(RHASH(obj
)->as
.ar
);
620 rb_bug("transient_heap_ptr: unknown obj %s\n", rb_obj_info(obj
));
628 transient_heap_promote_add(struct transient_heap
* theap
, VALUE obj
)
630 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, "rb_transient_heap_promote: %s\n", rb_obj_info(obj
));
632 if (TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
) {
633 /* duplicate check */
635 for (i
=0; i
<theap
->promoted_objects_index
; i
++) {
636 if (theap
->promoted_objects
[i
] == obj
) return;
640 if (theap
->promoted_objects_size
<= theap
->promoted_objects_index
) {
641 theap
->promoted_objects_size
*= 2;
642 if (TRANSIENT_HEAP_DEBUG
>= 1) fprintf(stderr
, "rb_transient_heap_promote: expand table to %d\n", theap
->promoted_objects_size
);
643 if (UNLIKELY((size_t)theap
->promoted_objects_size
> SIZE_MAX
/ sizeof(VALUE
))) {
644 /* realloc failure due to integer overflow */
645 theap
->promoted_objects
= NULL
;
648 theap
->promoted_objects
= realloc(theap
->promoted_objects
, theap
->promoted_objects_size
* sizeof(VALUE
));
650 if (theap
->promoted_objects
== NULL
) rb_bug("rb_transient_heap_promote: realloc failed");
652 theap
->promoted_objects
[theap
->promoted_objects_index
++] = obj
;
656 rb_transient_heap_promote(VALUE obj
)
660 if (transient_heap_ptr(obj
, FALSE
)) {
661 struct transient_heap
* theap
= transient_heap_get();
662 transient_heap_promote_add(theap
, obj
);
669 static struct transient_alloc_header
*
670 alloc_header(struct transient_heap_block
* block
, int index
)
672 return (void *)&block
->buff
[index
];
676 transient_heap_reset(void)
680 struct transient_heap
* theap
= transient_heap_get();
681 struct transient_heap_block
* block
;
683 if (TRANSIENT_HEAP_DEBUG
>= 1) fprintf(stderr
, "!! transient_heap_reset\n");
685 block
= theap
->marked_blocks
;
687 struct transient_heap_block
*next_block
= block
->info
.next_block
;
688 theap
->total_objects
-= block
->info
.objects
;
689 #if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
690 if (madvise(block
, TRANSIENT_HEAP_BLOCK_SIZE
, MADV_DONTNEED
) != 0) {
691 rb_bug("madvise err:%d", errno
);
693 if (mprotect(block
, TRANSIENT_HEAP_BLOCK_SIZE
, PROT_NONE
) != 0) {
694 rb_bug("mprotect err:%d", errno
);
698 connect_to_free_blocks(theap
, block
);
700 theap
->total_blocks
--;
704 if (TRANSIENT_HEAP_DEBUG
>= 1) fprintf(stderr
, "!! transient_heap_reset block_num:%d\n", theap
->total_blocks
);
706 theap
->marked_blocks
= NULL
;
707 theap
->total_marked_objects
= 0;
711 transient_heap_block_evacuate(struct transient_heap
* theap
, struct transient_heap_block
* block
)
713 int marked_index
= block
->info
.last_marked_index
;
714 block
->info
.last_marked_index
= TRANSIENT_HEAP_ALLOC_MARKING_LAST
;
716 while (marked_index
>= 0) {
717 struct transient_alloc_header
*header
= alloc_header(block
, marked_index
);
718 asan_unpoison_memory_region(header
, sizeof *header
, true);
719 VALUE obj
= header
->obj
;
720 TH_ASSERT(header
->magic
== TRANSIENT_HEAP_ALLOC_MAGIC
);
721 if (header
->magic
!= TRANSIENT_HEAP_ALLOC_MAGIC
) rb_bug("transient_heap_block_evacuate: wrong header %p %s\n", (void *)header
, rb_obj_info(obj
));
723 if (TRANSIENT_HEAP_DEBUG
>= 3) fprintf(stderr
, " * transient_heap_block_evacuate %p %s\n", (void *)header
, rb_obj_info(obj
));
726 RB_DEBUG_COUNTER_INC(theap_evacuate
);
728 switch (BUILTIN_TYPE(obj
)) {
730 rb_ary_transient_heap_evacuate(obj
, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
);
733 rb_obj_transient_heap_evacuate(obj
, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
);
736 rb_struct_transient_heap_evacuate(obj
, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
);
739 rb_hash_transient_heap_evacuate(obj
, !TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
);
742 rb_bug("unsupported: %s\n", rb_obj_info(obj
));
744 header
->obj
= Qundef
; /* for debug */
746 marked_index
= header
->next_marked_index
;
747 asan_poison_memory_region(header
, sizeof *header
);
751 #if USE_RUBY_DEBUG_LOG
753 transient_heap_status_cstr(enum transient_heap_status status
)
756 case transient_heap_none
: return "none";
757 case transient_heap_marking
: return "marking";
758 case transient_heap_escaping
: return "escaping";
760 UNREACHABLE_RETURN(NULL
);
765 transient_heap_update_status(struct transient_heap
* theap
, enum transient_heap_status status
)
767 RUBY_DEBUG_LOG("%s -> %s",
768 transient_heap_status_cstr(theap
->status
),
769 transient_heap_status_cstr(status
));
771 TH_ASSERT(theap
->status
!= status
);
772 theap
->status
= status
;
776 transient_heap_evacuate(void *dmy
)
778 struct transient_heap
* theap
= transient_heap_get();
780 if (theap
->total_marked_objects
== 0) return;
781 if (ruby_single_main_ractor
== NULL
) rb_bug("not single ractor mode");
782 if (theap
->status
== transient_heap_marking
) {
783 if (TRANSIENT_HEAP_DEBUG
>= 1) fprintf(stderr
, "!! transient_heap_evacuate: skip while transient_heap_marking\n");
786 VALUE gc_disabled
= rb_gc_disable_no_rest();
788 struct transient_heap_block
* block
;
790 RUBY_DEBUG_LOG("start gc_disabled:%d", RTEST(gc_disabled
));
792 if (TRANSIENT_HEAP_DEBUG
>= 1) {
794 fprintf(stderr
, "!! transient_heap_evacuate start total_blocks:%d\n", theap
->total_blocks
);
795 if (TRANSIENT_HEAP_DEBUG
>= 4) {
796 for (i
=0; i
<theap
->promoted_objects_index
; i
++) fprintf(stderr
, "%4d %s\n", i
, rb_obj_info(theap
->promoted_objects
[i
]));
799 if (TRANSIENT_HEAP_DEBUG
>= 2) transient_heap_dump(theap
);
801 TH_ASSERT(theap
->status
== transient_heap_none
);
802 transient_heap_update_status(theap
, transient_heap_escaping
);
804 /* evacuate from marked blocks */
805 block
= theap
->marked_blocks
;
807 transient_heap_block_evacuate(theap
, block
);
808 block
= block
->info
.next_block
;
811 /* evacuate from using blocks
812 only affect incremental marking */
813 block
= theap
->using_blocks
;
815 transient_heap_block_evacuate(theap
, block
);
816 block
= block
->info
.next_block
;
819 /* all objects in marked_objects are escaped. */
820 transient_heap_reset();
822 if (TRANSIENT_HEAP_DEBUG
> 0) {
823 fprintf(stderr
, "!! transient_heap_evacuate end total_blocks:%d\n", theap
->total_blocks
);
826 transient_heap_verify(theap
);
827 transient_heap_update_status(theap
, transient_heap_none
);
829 if (gc_disabled
!= Qtrue
) rb_gc_enable();
830 RUBY_DEBUG_LOG("finish");
835 rb_transient_heap_evacuate(void)
837 transient_heap_evacuate(NULL
);
841 clear_marked_index(struct transient_heap_block
* block
)
843 int marked_index
= block
->info
.last_marked_index
;
845 while (marked_index
!= TRANSIENT_HEAP_ALLOC_MARKING_LAST
) {
846 struct transient_alloc_header
*header
= alloc_header(block
, marked_index
);
847 /* header is poisoned to prevent buffer overflow, should
848 * unpoison first... */
849 asan_unpoison_memory_region(header
, sizeof *header
, false);
850 TH_ASSERT(marked_index
!= TRANSIENT_HEAP_ALLOC_MARKING_FREE
);
851 if (0) fprintf(stderr
, "clear_marked_index - block:%p mark_index:%d\n", (void *)block
, marked_index
);
853 marked_index
= header
->next_marked_index
;
854 header
->next_marked_index
= TRANSIENT_HEAP_ALLOC_MARKING_FREE
;
857 block
->info
.last_marked_index
= TRANSIENT_HEAP_ALLOC_MARKING_LAST
;
861 blocks_clear_marked_index(struct transient_heap_block
* block
)
864 clear_marked_index(block
);
865 block
= block
->info
.next_block
;
870 transient_heap_block_update_refs(struct transient_heap
* theap
, struct transient_heap_block
* block
)
872 int marked_index
= block
->info
.last_marked_index
;
874 while (marked_index
>= 0) {
875 struct transient_alloc_header
*header
= alloc_header(block
, marked_index
);
877 asan_unpoison_memory_region(header
, sizeof *header
, false);
879 header
->obj
= rb_gc_location(header
->obj
);
881 marked_index
= header
->next_marked_index
;
882 asan_poison_memory_region(header
, sizeof *header
);
887 transient_heap_blocks_update_refs(struct transient_heap
* theap
, struct transient_heap_block
*block
, const char *type_str
)
890 transient_heap_block_update_refs(theap
, block
);
891 block
= block
->info
.next_block
;
896 rb_transient_heap_update_references(void)
900 struct transient_heap
* theap
= transient_heap_get();
903 transient_heap_blocks_update_refs(theap
, theap
->using_blocks
, "using_blocks");
904 transient_heap_blocks_update_refs(theap
, theap
->marked_blocks
, "marked_blocks");
906 for (i
=0; i
<theap
->promoted_objects_index
; i
++) {
907 VALUE obj
= theap
->promoted_objects
[i
];
908 theap
->promoted_objects
[i
] = rb_gc_location(obj
);
913 rb_transient_heap_start_marking(int full_marking
)
916 RUBY_DEBUG_LOG("full?:%d", full_marking
);
918 struct transient_heap
* theap
= transient_heap_get();
920 if (TRANSIENT_HEAP_DEBUG
>= 1) fprintf(stderr
, "!! rb_transient_heap_start_marking objects:%d blocks:%d promoted:%d full_marking:%d\n",
921 theap
->total_objects
, theap
->total_blocks
, theap
->promoted_objects_index
, full_marking
);
922 if (TRANSIENT_HEAP_DEBUG
>= 2) transient_heap_dump(theap
);
924 blocks_clear_marked_index(theap
->marked_blocks
);
925 blocks_clear_marked_index(theap
->using_blocks
);
927 if (theap
->using_blocks
) {
928 if (theap
->using_blocks
->info
.objects
> 0) {
929 append_to_marked_blocks(theap
, theap
->using_blocks
);
930 theap
->using_blocks
= NULL
;
933 append_to_marked_blocks(theap
, theap
->using_blocks
->info
.next_block
);
934 theap
->using_blocks
->info
.next_block
= NULL
;
938 if (theap
->using_blocks
== NULL
) {
939 theap
->using_blocks
= transient_heap_allocatable_block(theap
);
942 TH_ASSERT(theap
->status
== transient_heap_none
);
943 transient_heap_update_status(theap
, transient_heap_marking
);
944 theap
->total_marked_objects
= 0;
947 theap
->promoted_objects_index
= 0;
949 else { /* mark promoted objects */
951 for (i
=0; i
<theap
->promoted_objects_index
; i
++) {
952 VALUE obj
= theap
->promoted_objects
[i
];
953 const void *ptr
= transient_heap_ptr(obj
, TRUE
);
955 rb_transient_heap_mark(obj
, ptr
);
960 transient_heap_verify(theap
);
964 rb_transient_heap_finish_marking(void)
967 struct transient_heap
* theap
= transient_heap_get();
969 RUBY_DEBUG_LOG("objects:%d, marked:%d",
970 theap
->total_objects
,
971 theap
->total_marked_objects
);
972 if (TRANSIENT_HEAP_DEBUG
>= 2) transient_heap_dump(theap
);
974 TH_ASSERT(theap
->total_objects
>= theap
->total_marked_objects
);
976 TH_ASSERT(theap
->status
== transient_heap_marking
);
977 transient_heap_update_status(theap
, transient_heap_none
);
979 if (theap
->total_marked_objects
> 0) {
980 if (TRANSIENT_HEAP_DEBUG
>= 1) fprintf(stderr
, "-> rb_transient_heap_finish_marking register escape func.\n");
981 rb_postponed_job_register_one(0, transient_heap_evacuate
, NULL
);
984 transient_heap_reset();
987 transient_heap_verify(theap
);
989 #endif /* USE_TRANSIENT_HEAP */