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"
20#include "ruby_assert.h"
21#include "transient_heap.h"
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)
40#define TRANSIENT_HEAP_DEBUG 0
46#define TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK 0
48#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
55#define TRANSIENT_HEAP_DEBUG_DONT_PROMOTE 0
58#define TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE 1024
61#define TRANSIENT_HEAP_BLOCK_SIZE (1024 * 32 )
62#ifndef TRANSIENT_HEAP_TOTAL_SIZE
63#define TRANSIENT_HEAP_TOTAL_SIZE (1024 * 1024 * 32)
65#define TRANSIENT_HEAP_ALLOC_MAX (1024 * 2 )
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
75enum transient_heap_status {
77 transient_heap_marking,
78 transient_heap_escaping
84 int16_t last_marked_index;
88 char buff[TRANSIENT_HEAP_USABLE_SIZE];
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;
111 int16_t next_marked_index;
118static void transient_heap_promote_add(
struct transient_heap* theap, VALUE obj);
119static const void *transient_heap_ptr(VALUE obj,
int error);
120static 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))
129 while (i<block->info.index) {
130 void *ptr = &block->buff[i];
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));
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;
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");
161rb_transient_heap_dump(
void)
163 transient_heap_dump(&global_transient_heap);
166#if TRANSIENT_HEAP_CHECK_MODE >= 2
167ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(
static void 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));
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);
202 n += transient_heap_block_verify(theap, block);
204 block = block->info.next_block;
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);
228rb_transient_heap_verify(
void)
230 transient_heap_verify(&global_transient_heap);
234transient_heap_get(
void)
237 transient_heap_verify(theap);
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);
255 block->info.next_block = theap->free_blocks;
256 theap->free_blocks = block;
262 block->info.next_block = theap->using_blocks;
263 theap->using_blocks = block;
270 block->info.next_block = theap->marked_blocks;
271 theap->marked_blocks = block;
278 if (theap->marked_blocks) {
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;
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);
327#if TRANSIENT_HEAP_DEBUG_INFINITE_BLOCK
328 block = transient_heap_block_alloc(theap);
329 theap->total_blocks++;
332 block = theap->free_blocks;
334 theap->free_blocks = block->info.next_block;
335 block->info.next_block = NULL;
336 theap->total_blocks++;
344transient_heap_allocatable_header(
struct transient_heap* theap,
size_t size)
349 TH_ASSERT(block->info.index <= (int16_t)TRANSIENT_HEAP_USABLE_SIZE);
351 if (TRANSIENT_HEAP_USABLE_SIZE - block->info.index >= size) {
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);
367rb_transient_heap_alloc(VALUE obj,
size_t req_size)
370 if (ruby_single_main_ractor == NULL)
return NULL;
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
387 if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr,
"rb_transient_heap_alloc: [promoted object] %s\n", rb_obj_info(obj));
392 if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr,
"rb_transient_heap_alloc: [hidden object] %s\n", rb_obj_info(obj));
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;
411 asan_poison_memory_region(header,
sizeof *header);
414 theap->total_objects++;
416#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
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);
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);
440Init_TransientHeap(
void)
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;
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.");
472 if (block->buff <= (
char *)header && (
char *)header < block->buff + TRANSIENT_HEAP_USABLE_SIZE) {
475 block = block->info.next_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");
500ptr_to_alloc_header(
const void *ptr)
508transient_header_managed_ptr_p(
struct transient_heap* theap,
const void *ptr)
510 if (alloc_header_to_block_verbose(theap, ptr_to_alloc_header(ptr))) {
520rb_transient_heap_managed_ptr_p(
const void *ptr)
522 return transient_header_managed_ptr_p(transient_heap_get(), ptr);
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));
543rb_transient_heap_mark(VALUE obj,
const void *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
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) {
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) {
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);
586ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(
static const void *transient_heap_ptr(VALUE obj,
int error));
588transient_heap_ptr(VALUE obj,
int error)
590 const void *ptr = NULL;
596 ptr =
RARRAY(obj)->as.heap.ptr;
600 if (ROBJ_TRANSIENT_P(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));
628transient_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) {
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))) {
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;
656rb_transient_heap_promote(VALUE obj)
660 if (transient_heap_ptr(obj, FALSE)) {
662 transient_heap_promote_add(theap, obj);
672 return (
void *)&block->buff[index];
676transient_heap_reset(
void)
683 if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr,
"!! transient_heap_reset\n");
685 block = theap->marked_blocks;
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;
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) {
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);
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));
746 marked_index = header->next_marked_index;
747 asan_poison_memory_region(header,
sizeof *header);
751#if defined(USE_RUBY_DEBUG_LOG) && USE_RUBY_DEBUG_LOG
753transient_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";
765transient_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;
776transient_heap_evacuate(
void *dmy)
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();
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);
805 block = theap->marked_blocks;
807 transient_heap_block_evacuate(theap, block);
808 block = block->info.next_block;
813 block = theap->using_blocks;
815 transient_heap_block_evacuate(theap, block);
816 block = block->info.next_block;
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);
830 RUBY_DEBUG_LOG(
"finish");
835rb_transient_heap_evacuate(
void)
837 transient_heap_evacuate(NULL);
843 int marked_index = block->info.last_marked_index;
845 while (marked_index != TRANSIENT_HEAP_ALLOC_MARKING_LAST) {
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;
864 clear_marked_index(block);
865 block = block->info.next_block;
872 int marked_index = block->info.last_marked_index;
874 while (marked_index >= 0) {
877 asan_unpoison_memory_region(header,
sizeof *header,
false);
881 marked_index = header->next_marked_index;
882 asan_poison_memory_region(header,
sizeof *header);
890 transient_heap_block_update_refs(theap, block);
891 block = block->info.next_block;
896rb_transient_heap_update_references(
void)
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];
913rb_transient_heap_start_marking(
int full_marking)
916 RUBY_DEBUG_LOG(
"full?:%d", full_marking);
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;
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);
964rb_transient_heap_finish_marking(
void)
971 RUBY_DEBUG_LOG(
"objects:%d, marked:%d",
972 theap->total_objects,
973 theap->total_marked_objects);
974 if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
976 TH_ASSERT(theap->total_objects >= theap->total_marked_objects);
978 TH_ASSERT(theap->status == transient_heap_marking);
979 transient_heap_update_status(theap, transient_heap_none);
981 if (theap->total_marked_objects > 0) {
982 if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr,
"-> rb_transient_heap_finish_marking register escape func.\n");
986 transient_heap_reset();
989 transient_heap_verify(theap);
int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
Identical to rb_postponed_job_register_one(), except it additionally checks for duplicated registrati...
#define Qundef
Old name of RUBY_Qundef.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
#define T_HASH
Old name of RUBY_T_HASH.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
void rb_bug(const char *fmt,...)
Interpreter panic switch.
VALUE rb_gc_enable(void)
(Re-) enables GC.
VALUE rb_gc_location(VALUE obj)
Finds a new "location" of an object.
#define RARRAY(obj)
Convenient casting macro.
static bool RARRAY_TRANSIENT_P(VALUE ary)
Queries if the array is a transient array.
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
static bool RB_OBJ_PROMOTED_RAW(VALUE obj)
This is the implementation of RB_OBJ_PROMOTED().
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
#define RTEST
This is an old name of RB_TEST.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.