Bug #14858 ยป transient_heap.patch
array.c (working copy) | ||
---|---|---|
#include "probes.h"
|
||
#include "id.h"
|
||
#include "debug_counter.h"
|
||
#include "gc.h"
|
||
|
||
// #define ARRAY_DEBUG
|
||
|
||
#ifndef ARRAY_DEBUG
|
||
# define NDEBUG
|
||
... | ... | |
#define FL_SET_EMBED(a) do { \
|
||
assert(!ARY_SHARED_P(a)); \
|
||
FL_SET((a), RARRAY_EMBED_FLAG); \
|
||
FL_UNSET_RAW((a), RARRAY_TRANSIENT_FLAG); \
|
||
ary_verify(a); \
|
||
} while (0)
|
||
#define FL_UNSET_EMBED(ary) FL_UNSET((ary), RARRAY_EMBED_FLAG|RARRAY_EMBED_LEN_MASK)
|
||
#define FL_SET_SHARED(ary) do { \
|
||
... | ... | |
} while (0)
|
||
#define FL_SET_SHARED_ROOT(ary) do { \
|
||
assert(!ARY_EMBED_P(ary)); \
|
||
assert(!ARY_TRANSIENT_P(ary)); \
|
||
FL_SET((ary), RARRAY_SHARED_ROOT_FLAG); \
|
||
} while (0)
|
||
|
||
#define ARY_SET(a, i, v) RARRAY_ASET((assert(!ARY_SHARED_P(a)), (a)), (i), (v))
|
||
|
||
|
||
#ifdef ARRAY_DEBUG
|
||
#define ary_verify(ary) ary_verify_(ary, __FILE__, __LINE__)
|
||
|
||
static void
|
||
ary_verify_(VALUE ary, const char *file, int line)
|
||
{
|
||
if (FL_TEST(ary, ELTS_SHARED)) {
|
||
VALUE root = RARRAY(ary)->as.heap.aux.shared;
|
||
const VALUE *ptr = RARRAY_CONST_PTR(ary);
|
||
const VALUE *root_ptr = RARRAY_CONST_PTR(root);
|
||
long len = RARRAY_LEN(ary), root_len = RARRAY_LEN(root);
|
||
assert(FL_TEST(root, RARRAY_SHARED_ROOT_FLAG));
|
||
assert(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
|
||
ary_verify(root);
|
||
}
|
||
else if (ARY_EMBED_P(ary)) {
|
||
assert(!ARY_TRANSIENT_P(ary));
|
||
assert(!ARY_SHARED_P(ary));
|
||
assert(RARRAY_LEN(ary) <= RARRAY_EMBED_LEN_MAX);
|
||
}
|
||
else {
|
||
#if 0
|
||
const VALUE *ptr = RARRAY_CONST_PTR(ary);
|
||
long i, len = RARRAY_LEN(ary);
|
||
volatile VALUE v;
|
||
for (i=0; i<len; i++) {
|
||
v = ptr[i]; // access check
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
#else
|
||
#define ary_verify(ary) ((void)0)
|
||
#endif
|
||
|
||
void
|
||
rb_mem_clear(register VALUE *mem, register long size)
|
||
{
|
||
... | ... | |
ary_memcpy0(ary, beg, argc, argv, ary);
|
||
}
|
||
|
||
void *rb_transient_heap_alloc(VALUE obj, size_t size);
|
||
|
||
static VALUE *
|
||
ary_heap_alloc(VALUE ary, size_t capa)
|
||
{
|
||
VALUE *ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * capa);
|
||
|
||
if (ptr != NULL) {
|
||
FL_SET_RAW(ary, RARRAY_TRANSIENT_FLAG);
|
||
//fprintf(stderr, "ary:%p (%p) is TRANSIENT.\n", (void *)ary, ptr);
|
||
}
|
||
else {
|
||
FL_UNSET_RAW(ary, RARRAY_TRANSIENT_FLAG);
|
||
ptr = ALLOC_N(VALUE, capa);
|
||
//fprintf(stderr, "ary:%p (%p) is not TRANSIENT.\n", (void *)ary, ptr);
|
||
}
|
||
|
||
return ptr;
|
||
}
|
||
|
||
static void
|
||
ary_heap_free_ptr(VALUE ary, const VALUE *ptr, long size)
|
||
{
|
||
if (ARY_TRANSIENT_P(ary)) {
|
||
/* ignore it */
|
||
// fprintf(stderr, "ary_heap_free: %p is transient.\n", (void *)ary);
|
||
}
|
||
else {
|
||
// fprintf(stderr, "ary_heap_free: %p is freed.\n", (void *)ary);
|
||
ruby_sized_xfree((void *)ptr, size);
|
||
}
|
||
}
|
||
|
||
static void
|
||
ary_heap_free(VALUE ary)
|
||
{
|
||
// fprintf(stderr, "ary_heap_free: %p\n", (void *)ary);
|
||
if (ARY_TRANSIENT_P(ary)) {
|
||
/* ignore */
|
||
}
|
||
else {
|
||
ary_heap_free_ptr(ary, ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
|
||
}
|
||
}
|
||
|
||
static void
|
||
ary_heap_realloc(VALUE ary, size_t new_capa)
|
||
{
|
||
size_t old_capa = RARRAY(ary)->as.heap.aux.capa;
|
||
|
||
if (ARY_TRANSIENT_P(ary)) {
|
||
if (new_capa <= old_capa) {
|
||
/* do nothing */
|
||
}
|
||
else {
|
||
VALUE *new_ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * new_capa);
|
||
|
||
if (new_ptr == NULL) {
|
||
new_ptr = ALLOC_N(VALUE, new_capa);
|
||
FL_UNSET_RAW(ary, RARRAY_TRANSIENT_FLAG);
|
||
}
|
||
|
||
MEMCPY(new_ptr, ARY_HEAP_PTR(ary), VALUE, old_capa);
|
||
ARY_SET_PTR(ary, new_ptr);
|
||
}
|
||
}
|
||
else {
|
||
SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, new_capa, old_capa);
|
||
}
|
||
}
|
||
|
||
void
|
||
rb_ary_detransient(VALUE ary)
|
||
{
|
||
// fprintf(stderr, "rb_ary_detransient:\n");
|
||
// fprintf(stderr, "(1) %s\n", rb_obj_info(ary));
|
||
|
||
if (ARY_TRANSIENT_P(ary)) {
|
||
VALUE *new_ptr;
|
||
long capa = RARRAY(ary)->as.heap.aux.capa;
|
||
long len = RARRAY(ary)->as.heap.len;
|
||
|
||
assert(ARY_OWNS_HEAP_P(ary));
|
||
assert(ARY_TRANSIENT_P(ary));
|
||
|
||
if (ARY_SHARED_ROOT_P(ary)) {
|
||
capa = len;
|
||
}
|
||
|
||
new_ptr = ALLOC_N(VALUE, capa);
|
||
MEMCPY(new_ptr, ARY_HEAP_PTR(ary), VALUE, capa);
|
||
RARRAY(ary)->as.heap.ptr = new_ptr;
|
||
/* do not use ARY_SET_PTR() because they assert !frozen */
|
||
FL_UNSET_RAW(ary, RARRAY_TRANSIENT_FLAG);
|
||
|
||
// fprintf(stderr, "(2) %s\n", rb_obj_info(ary));
|
||
}
|
||
}
|
||
|
||
static void
|
||
ary_resize_capa(VALUE ary, long capacity)
|
||
{
|
||
assert(RARRAY_LEN(ary) <= capacity);
|
||
assert(!OBJ_FROZEN(ary));
|
||
assert(!ARY_SHARED_P(ary));
|
||
|
||
// fprintf(stderr, "ary_resize_capa (%ld): %s\n", capacity, rb_obj_info(ary));
|
||
|
||
if (capacity > RARRAY_EMBED_LEN_MAX) {
|
||
if (ARY_EMBED_P(ary)) {
|
||
long len = ARY_EMBED_LEN(ary);
|
||
VALUE *ptr = ALLOC_N(VALUE, (capacity));
|
||
VALUE *ptr = ary_heap_alloc(ary, capacity);
|
||
|
||
MEMCPY(ptr, ARY_EMBED_PTR(ary), VALUE, len);
|
||
FL_UNSET_EMBED(ary);
|
||
ARY_SET_PTR(ary, ptr);
|
||
ARY_SET_HEAP_LEN(ary, len);
|
||
}
|
||
else {
|
||
SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity, RARRAY(ary)->as.heap.aux.capa);
|
||
// fprintf(stderr, "ary_resize_capa %s\n", rb_obj_info(ary));
|
||
ary_heap_realloc(ary, capacity);
|
||
}
|
||
ARY_SET_CAPA(ary, (capacity));
|
||
ARY_SET_CAPA(ary, capacity);
|
||
// fprintf(stderr, "-> ary_resize_capa: %s\n", rb_obj_info(ary));
|
||
|
||
// fprintf(stderr, "ary_resize_capa %p len:%ld capa:%ld - %s\n", (void *)ary, RARRAY_LEN(ary), capacity, rb_obj_info(ary));
|
||
}
|
||
else {
|
||
if (!ARY_EMBED_P(ary)) {
|
||
long len = RARRAY_LEN(ary);
|
||
long old_capa = RARRAY(ary)->as.heap.aux.capa;
|
||
const VALUE *ptr = RARRAY_CONST_PTR(ary);
|
||
|
||
if (len > capacity) len = capacity;
|
||
MEMCPY((VALUE *)RARRAY(ary)->as.ary, ptr, VALUE, len);
|
||
ary_heap_free_ptr(ary, ptr, old_capa);
|
||
|
||
FL_SET_EMBED(ary);
|
||
ARY_SET_LEN(ary, len);
|
||
ruby_sized_xfree((VALUE *)ptr, RARRAY(ary)->as.heap.aux.capa);
|
||
|
||
// fprintf(stderr, "ary_resize_capa: heap->embed %p len:%ld\n", (void *)ary, len);
|
||
}
|
||
}
|
||
|
||
ary_verify(ary);
|
||
}
|
||
|
||
static inline void
|
||
... | ... | |
long old_capa = RARRAY(ary)->as.heap.aux.capa;
|
||
assert(!ARY_SHARED_P(ary));
|
||
assert(old_capa >= capacity);
|
||
if (old_capa > capacity)
|
||
SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity, old_capa);
|
||
if (old_capa > capacity) ary_heap_realloc(ary, capacity);
|
||
|
||
ary_verify(ary);
|
||
}
|
||
|
||
static void
|
||
... | ... | |
new_capa = (ARY_MAX_SIZE - min) / 2;
|
||
}
|
||
new_capa += min;
|
||
// fprintf(stderr, "ary_double_capa: %p %d\n", (void *)ary, FL_TEST(ary, RARRAY_TRANSIENT_FLAG) ? 1 : 0);
|
||
ary_resize_capa(ary, new_capa);
|
||
|
||
ary_verify(ary);
|
||
}
|
||
|
||
static void
|
||
... | ... | |
VALUE shared = RARRAY(ary)->as.heap.aux.shared;
|
||
rb_ary_decrement_share(shared);
|
||
FL_UNSET_SHARED(ary);
|
||
|
||
ary_verify(ary);
|
||
}
|
||
|
||
static inline void
|
||
... | ... | |
rb_ary_modify_check(VALUE ary)
|
||
{
|
||
rb_check_frozen(ary);
|
||
ary_verify(ary);
|
||
}
|
||
|
||
void
|
||
... | ... | |
rb_ary_decrement_share(shared);
|
||
}
|
||
else {
|
||
VALUE *ptr = ALLOC_N(VALUE, len);
|
||
VALUE *ptr = ary_heap_alloc(ary, len);
|
||
MEMCPY(ptr, RARRAY_CONST_PTR(ary), VALUE, len);
|
||
rb_ary_unshare(ary);
|
||
ARY_SET_CAPA(ary, len);
|
||
... | ... | |
|
||
rb_gc_writebarrier_remember(ary);
|
||
}
|
||
ary_verify(ary);
|
||
}
|
||
|
||
static VALUE
|
||
... | ... | |
if (ARY_SHARED_OCCUPIED(shared)) {
|
||
if (RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared) + new_len <= RARRAY_LEN(shared)) {
|
||
rb_ary_modify_check(ary);
|
||
|
||
ary_verify(shared);
|
||
return shared;
|
||
}
|
||
else {
|
||
... | ... | |
if (new_len > capa - (capa >> 6)) {
|
||
ary_double_capa(ary, new_len);
|
||
}
|
||
|
||
ary_verify(ary);
|
||
return ary;
|
||
}
|
||
}
|
||
... | ... | |
ary_double_capa(ary, new_len);
|
||
}
|
||
|
||
ary_verify(ary);
|
||
return ary;
|
||
}
|
||
|
||
... | ... | |
return ary_alloc(klass);
|
||
}
|
||
|
||
void rb_transient_heap_dump(void);
|
||
|
||
static VALUE
|
||
ary_new(VALUE klass, long capa)
|
||
{
|
||
... | ... | |
|
||
ary = ary_alloc(klass);
|
||
if (capa > RARRAY_EMBED_LEN_MAX) {
|
||
ptr = ALLOC_N(VALUE, capa);
|
||
ptr = ary_heap_alloc(ary, capa);
|
||
FL_UNSET_EMBED(ary);
|
||
ARY_SET_PTR(ary, ptr);
|
||
ARY_SET_CAPA(ary, capa);
|
||
... | ... | |
VALUE
|
||
rb_ary_tmp_new(long capa)
|
||
{
|
||
return ary_new(0, capa);
|
||
VALUE ary = ary_new(0, capa);
|
||
rb_ary_detransient(ary);
|
||
return ary;
|
||
}
|
||
|
||
VALUE
|
||
... | ... | |
{
|
||
if (ARY_OWNS_HEAP_P(ary)) {
|
||
RB_DEBUG_COUNTER_INC(obj_ary_ptr);
|
||
ruby_sized_xfree((void *)ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
|
||
ary_heap_free(ary);
|
||
}
|
||
else {
|
||
RB_DEBUG_COUNTER_INC(obj_ary_embed);
|
||
... | ... | |
{
|
||
rb_ary_free(ary);
|
||
RBASIC(ary)->flags |= RARRAY_EMBED_FLAG;
|
||
RBASIC(ary)->flags &= ~RARRAY_EMBED_LEN_MASK;
|
||
RBASIC(ary)->flags &= ~(RARRAY_EMBED_LEN_MASK | RARRAY_TRANSIENT_FLAG);
|
||
}
|
||
|
||
static VALUE
|
||
ary_make_shared(VALUE ary)
|
||
{
|
||
assert(!ARY_EMBED_P(ary));
|
||
ary_verify(ary);
|
||
|
||
if (ARY_SHARED_P(ary)) {
|
||
return ARY_SHARED(ary);
|
||
}
|
||
... | ... | |
return ary;
|
||
}
|
||
else if (OBJ_FROZEN(ary)) {
|
||
rb_ary_detransient(ary);
|
||
ary_shrink_capa(ary);
|
||
FL_SET_SHARED_ROOT(ary);
|
||
ARY_SET_SHARED_NUM(ary, 1);
|
||
... | ... | |
}
|
||
else {
|
||
long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary);
|
||
const VALUE *ptr;
|
||
NEWOBJ_OF(shared, struct RArray, 0, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0));
|
||
FL_UNSET_EMBED(shared);
|
||
|
||
rb_ary_detransient(ary);
|
||
ptr = ARY_HEAP_PTR(ary);
|
||
|
||
FL_UNSET_EMBED(shared);
|
||
ARY_SET_LEN((VALUE)shared, capa);
|
||
ARY_SET_PTR((VALUE)shared, RARRAY_CONST_PTR(ary));
|
||
ARY_SET_PTR((VALUE)shared, ptr);
|
||
ary_mem_clear((VALUE)shared, len, capa - len);
|
||
FL_SET_SHARED_ROOT(shared);
|
||
ARY_SET_SHARED_NUM((VALUE)shared, 1);
|
||
FL_SET_SHARED(ary);
|
||
ARY_SET_SHARED(ary, (VALUE)shared);
|
||
OBJ_FREEZE(shared);
|
||
|
||
ary_verify(ary);
|
||
return (VALUE)shared;
|
||
}
|
||
}
|
||
... | ... | |
rb_ary_modify(ary);
|
||
if (argc == 0) {
|
||
if (ARY_OWNS_HEAP_P(ary) && RARRAY_CONST_PTR(ary) != 0) {
|
||
ruby_sized_xfree((void *)RARRAY_CONST_PTR(ary), ARY_HEAP_SIZE(ary));
|
||
ary_heap_free(ary);
|
||
}
|
||
rb_ary_unshare_safe(ary);
|
||
FL_SET_EMBED(ary);
|
||
... | ... | |
return ary;
|
||
}
|
||
|
||
static VALUE
|
||
ary_initialize_copy(VALUE self, VALUE orig)
|
||
{
|
||
FL_UNSET(self,
|
||
FL_USER1 | FL_USER2 | FL_USER3 | /* embed */
|
||
FL_USER5 | /* shard */
|
||
FL_USER13);
|
||
|
||
return rb_ary_replace(self, orig);
|
||
}
|
||
|
||
/*
|
||
* Returns a new array populated with the given objects.
|
||
*
|
||
... | ... | |
|
||
ARY_INCREASE_PTR(result, offset);
|
||
ARY_SET_LEN(result, len);
|
||
|
||
ary_verify(result);
|
||
return result;
|
||
}
|
||
}
|
||
... | ... | |
}
|
||
--n;
|
||
ARY_SET_LEN(ary, n);
|
||
ary_verify(ary);
|
||
return RARRAY_AREF(ary, n);
|
||
}
|
||
|
||
... | ... | |
rb_ary_modify_check(ary);
|
||
result = ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
|
||
ARY_INCREASE_LEN(ary, -RARRAY_LEN(result));
|
||
ary_verify(ary);
|
||
return result;
|
||
}
|
||
|
||
... | ... | |
MEMMOVE(ptr, ptr+1, VALUE, len-1);
|
||
}); /* WB: no new reference */
|
||
ARY_INCREASE_LEN(ary, -1);
|
||
ary_verify(ary);
|
||
return top;
|
||
}
|
||
assert(!ARY_EMBED_P(ary)); /* ARY_EMBED_LEN_MAX < ARY_DEFAULT_SIZE */
|
||
... | ... | |
ARY_INCREASE_PTR(ary, 1); /* shift ptr */
|
||
ARY_INCREASE_LEN(ary, -1);
|
||
|
||
ary_verify(ary);
|
||
|
||
return top;
|
||
}
|
||
|
||
... | ... | |
}
|
||
ARY_INCREASE_LEN(ary, -n);
|
||
|
||
ary_verify(ary);
|
||
return result;
|
||
}
|
||
|
||
... | ... | |
|
||
/* use shared array for big "queues" */
|
||
if (new_len > ARY_DEFAULT_SIZE * 4) {
|
||
ary_verify(ary);
|
||
|
||
/* make a room for unshifted items */
|
||
capa = ARY_CAPA(ary);
|
||
ary_make_shared(ary);
|
||
... | ... | |
}
|
||
ARY_SET_PTR(ary, head - argc);
|
||
assert(ARY_SHARED_OCCUPIED(ARY_SHARED(ary)));
|
||
|
||
ary_verify(ary);
|
||
return ARY_SHARED(ary);
|
||
}
|
||
else {
|
||
... | ... | |
MEMMOVE(ptr + argc, ptr, VALUE, len);
|
||
});
|
||
|
||
ary_verify(ary);
|
||
return ary;
|
||
}
|
||
}
|
||
... | ... | |
}
|
||
else {
|
||
if (olen > len + ARY_DEFAULT_SIZE) {
|
||
SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, len, RARRAY(ary)->as.heap.aux.capa);
|
||
ary_heap_realloc(ary, len);
|
||
ARY_SET_CAPA(ary, len);
|
||
}
|
||
ARY_SET_HEAP_LEN(ary, len);
|
||
}
|
||
ary_verify(ary);
|
||
return ary;
|
||
}
|
||
|
||
... | ... | |
rb_ary_unshare(ary);
|
||
}
|
||
else {
|
||
ruby_sized_xfree((void *)ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
|
||
ary_heap_free(ary);
|
||
}
|
||
ARY_SET_PTR(ary, RARRAY_CONST_PTR(tmp));
|
||
ARY_SET_HEAP_LEN(ary, len);
|
||
... | ... | |
MEMMOVE(ptr+pos, ptr+pos+1, VALUE, len-pos-1);
|
||
});
|
||
ARY_INCREASE_LEN(ary, -1);
|
||
|
||
ary_verify(ary);
|
||
return del;
|
||
}
|
||
|
||
... | ... | |
|
||
for (i = 0; i < RARRAY_LEN(orig); i++) {
|
||
VALUE v = RARRAY_AREF(orig, i);
|
||
|
||
if (!RTEST(rb_yield(v))) {
|
||
rb_ary_push(result, v);
|
||
}
|
||
... | ... | |
VALUE shared = 0;
|
||
|
||
if (ARY_OWNS_HEAP_P(copy)) {
|
||
RARRAY_PTR_USE(copy, ptr, ruby_sized_xfree(ptr, ARY_HEAP_SIZE(copy)));
|
||
ary_heap_free(copy);
|
||
}
|
||
else if (ARY_SHARED_P(copy)) {
|
||
shared = ARY_SHARED(copy);
|
||
... | ... | |
else {
|
||
VALUE shared = ary_make_shared(orig);
|
||
if (ARY_OWNS_HEAP_P(copy)) {
|
||
RARRAY_PTR_USE(copy, ptr, ruby_sized_xfree(ptr, ARY_HEAP_SIZE(copy)));
|
||
ary_heap_free(copy);
|
||
}
|
||
else {
|
||
rb_ary_unshare_safe(copy);
|
||
... | ... | |
rb_ary_clear(VALUE ary)
|
||
{
|
||
rb_ary_modify_check(ary);
|
||
ARY_SET_LEN(ary, 0);
|
||
if (ARY_SHARED_P(ary)) {
|
||
if (!ARY_EMBED_P(ary)) {
|
||
rb_ary_unshare(ary);
|
||
FL_SET_EMBED(ary);
|
||
ARY_SET_EMBED_LEN(ary, 0);
|
||
}
|
||
}
|
||
else if (ARY_DEFAULT_SIZE * 2 < ARY_CAPA(ary)) {
|
||
else {
|
||
ARY_SET_LEN(ary, 0);
|
||
if (ARY_DEFAULT_SIZE * 2 < ARY_CAPA(ary)) {
|
||
ary_resize_capa(ary, ARY_DEFAULT_SIZE * 2);
|
||
}
|
||
}
|
||
return ary;
|
||
}
|
||
|
||
... | ... | |
rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
|
||
rb_define_singleton_method(rb_cArray, "try_convert", rb_ary_s_try_convert, 1);
|
||
rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
|
||
rb_define_method(rb_cArray, "initialize_copy", rb_ary_replace, 1);
|
||
rb_define_method(rb_cArray, "initialize_copy", ary_initialize_copy, 1);
|
||
|
||
rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0);
|
||
rb_define_alias(rb_cArray, "to_s", "inspect");
|
common.mk (working copy) | ||
---|---|---|
thread.$(OBJEXT) \
|
||
time.$(OBJEXT) \
|
||
transcode.$(OBJEXT) \
|
||
transient_heap.$(OBJEXT) \
|
||
util.$(OBJEXT) \
|
||
variable.$(OBJEXT) \
|
||
version.$(OBJEXT) \
|
||
... | ... | |
transcode.$(OBJEXT): {$(VPATH)}subst.h
|
||
transcode.$(OBJEXT): {$(VPATH)}transcode.c
|
||
transcode.$(OBJEXT): {$(VPATH)}transcode_data.h
|
||
transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.c
|
||
util.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||
util.$(OBJEXT): $(top_srcdir)/include/ruby.h
|
||
util.$(OBJEXT): {$(VPATH)}config.h
|
gc.c (working copy) | ||
---|---|---|
void rb_iseq_mark(const rb_iseq_t *iseq);
|
||
void rb_iseq_free(const rb_iseq_t *iseq);
|
||
|
||
void rb_transient_heap_start_marking(int full_marking);
|
||
void rb_transient_heap_finish_marking(void);
|
||
void rb_transient_heap_mark(VALUE obj, const void *ptr);
|
||
void rb_transient_heap_promote(VALUE obj);
|
||
|
||
void rb_gcdebug_print_obj_condition(VALUE obj);
|
||
|
||
static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
|
||
... | ... | |
{
|
||
MARK_IN_BITMAP(&page->uncollectible_bits[0], obj);
|
||
objspace->rgengc.old_objects++;
|
||
rb_transient_heap_promote(obj);
|
||
|
||
#if RGENGC_PROFILE >= 2
|
||
objspace->profile.total_promoted_count++;
|
||
... | ... | |
|
||
case T_ARRAY:
|
||
if (FL_TEST(obj, ELTS_SHARED)) {
|
||
gc_mark(objspace, any->as.array.as.heap.aux.shared);
|
||
VALUE root = any->as.array.as.heap.aux.shared;
|
||
gc_mark(objspace, root);
|
||
}
|
||
else {
|
||
long i, len = RARRAY_LEN(obj);
|
||
const VALUE *ptr = RARRAY_CONST_PTR(obj);
|
||
for (i=0; i < len; i++) {
|
||
gc_mark(objspace, *ptr++);
|
||
gc_mark(objspace, ptr[i]);
|
||
}
|
||
|
||
if (objspace->mark_func_data == NULL) {
|
||
if (!FL_TEST_RAW(obj, RARRAY_EMBED_FLAG) &&
|
||
ARY_TRANSIENT_P(obj)) {
|
||
rb_transient_heap_mark(obj, ptr);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
... | ... | |
#endif
|
||
}
|
||
|
||
rb_transient_heap_finish_marking();
|
||
|
||
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_MARK, 0);
|
||
|
||
return TRUE;
|
||
... | ... | |
objspace->profile.heap_used_at_gc_start = heap_allocated_pages;
|
||
gc_prof_setup_new_record(objspace, reason);
|
||
gc_reset_malloc_info(objspace);
|
||
rb_transient_heap_start_marking(do_full_mark);
|
||
|
||
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */);
|
||
GC_ASSERT(during_gc);
|
||
... | ... | |
#if USE_RGENGC
|
||
const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
|
||
|
||
if (is_pointer_to_heap(&rb_objspace, (void *)obj)) {
|
||
snprintf(buff, buff_size, "%p [%d%s%s%s%s] %s",
|
||
(void *)obj, age,
|
||
C(RVALUE_UNCOLLECTIBLE_BITMAP(obj), "L"),
|
||
... | ... | |
C(RVALUE_MARKING_BITMAP(obj), "R"),
|
||
C(RVALUE_WB_UNPROTECTED_BITMAP(obj), "U"),
|
||
obj_type_name(obj));
|
||
}
|
||
else {
|
||
/* fake */
|
||
snprintf(buff, buff_size, "%p [%dXXXX] %s",
|
||
(void *)obj, age,
|
||
obj_type_name(obj));
|
||
}
|
||
#else
|
||
snprintf(buff, buff_size, "%p [%s] %s",
|
||
(void *)obj,
|
||
... | ... | |
UNEXPECTED_NODE(rb_raw_obj_info);
|
||
break;
|
||
case T_ARRAY:
|
||
snprintf(buff, buff_size, "%s [%s%s] len: %d", buff,
|
||
if (FL_TEST(obj, ELTS_SHARED)) {
|
||
snprintf(buff, buff_size, "%s shared -> %s", buff,
|
||
rb_obj_info(RARRAY(obj)->as.heap.aux.shared));
|
||
}
|
||
else if (FL_TEST(obj, RARRAY_EMBED_FLAG)) {
|
||
snprintf(buff, buff_size, "%s [%s%s] len: %d (embed)", buff,
|
||
C(ARY_EMBED_P(obj), "E"),
|
||
C(ARY_SHARED_P(obj), "S"),
|
||
(int)RARRAY_LEN(obj));
|
||
}
|
||
else {
|
||
snprintf(buff, buff_size, "%s [%s%s%s] len: %d, capa:%d ptr:%p", buff,
|
||
C(ARY_EMBED_P(obj), "E"),
|
||
C(ARY_SHARED_P(obj), "S"),
|
||
C(ARY_TRANSIENT_P(obj), "T"),
|
||
(int)RARRAY_LEN(obj),
|
||
ARY_EMBED_P(obj) ? -1 : (int)RARRAY(obj)->as.heap.aux.capa,
|
||
RARRAY_CONST_PTR(obj));
|
||
}
|
||
break;
|
||
case T_STRING: {
|
||
snprintf(buff, buff_size, "%s %s", buff, RSTRING_PTR(obj));
|
inits.c (working copy) | ||
---|---|---|
void
|
||
rb_call_inits(void)
|
||
{
|
||
CALL(TransientHeap);
|
||
CALL(Method);
|
||
CALL(RandomSeedCore);
|
||
CALL(sym);
|
internal.h (working copy) | ||
---|---|---|
struct vtm; /* defined by timev.h */
|
||
|
||
/* array.c */
|
||
#define RARRAY_TRANSIENT_FLAG FL_USER13
|
||
#define ARY_TRANSIENT_P(ary) FL_TEST_RAW((ary), RARRAY_TRANSIENT_FLAG)
|
||
|
||
VALUE rb_ary_last(int, const VALUE *, VALUE);
|
||
void rb_ary_set_len(VALUE, long);
|
||
void rb_ary_delete_same(VALUE, VALUE);
|
transient_heap.c (working copy) | ||
---|---|---|
#include "ruby/ruby.h"
|
||
#include "ruby/debug.h"
|
||
#include "vm_debug.h"
|
||
#include "gc.h"
|
||
#include "internal.h"
|
||
#include <sys/mman.h>
|
||
#include <errno.h>
|
||
#include "ruby_assert.h"
|
||
|
||
/*
|
||
* 1: enable assertions
|
||
* 2: enable verify
|
||
*/
|
||
#ifndef TRANSIENT_HEAP_CHECK_MODE
|
||
#define TRANSIENT_HEAP_CHECK_MODE 0
|
||
#endif
|
||
#define TH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(TRANSIENT_HEAP_CHECK_MODE > 0, expr, #expr)
|
||
|
||
/*
|
||
* 1: show events
|
||
* 2: show dump at events
|
||
* 3: show all operations
|
||
*/
|
||
#define TRANSIENT_HEAP_DEBUG 0
|
||
|
||
/* Provide blocks infinitely for debug.
|
||
* This mode generates blocks unlimitedly
|
||
* and prohibit access free'ed blocks to check invalid access.
|
||
*/
|
||
#define TRANSIENT_HEAP_INFINITE_BLOCK_MODE 0
|
||
|
||
enum transient_heap_status {
|
||
transient_heap_none,
|
||
transient_heap_marking,
|
||
transient_heap_escaping
|
||
};
|
||
|
||
struct transient_heap_block {
|
||
struct transient_heap_block_header {
|
||
int16_t size; /* sizeof(block) = TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header) */
|
||
int16_t index;
|
||
int16_t last_marked_index;
|
||
int16_t objects;
|
||
struct transient_heap_block *next_block;
|
||
} info;
|
||
char buff[];
|
||
};
|
||
|
||
struct transient_heap {
|
||
struct transient_heap_block *using_blocks;
|
||
struct transient_heap_block *marked_blocks;
|
||
struct transient_heap_block *free_blocks;
|
||
int total_objects;
|
||
int total_marked_objects;
|
||
int total_blocks;
|
||
enum transient_heap_status status;
|
||
|
||
VALUE *promoted_objects;
|
||
int promoted_objects_size;
|
||
int promoted_objects_index;
|
||
};
|
||
|
||
struct transient_alloc_header {
|
||
uint16_t magic;
|
||
uint16_t size;
|
||
int16_t next_marked_index;
|
||
int16_t dummy;
|
||
VALUE obj;
|
||
};
|
||
|
||
static struct transient_heap global_transient_heap;
|
||
|
||
|
||
#define TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE 1024
|
||
|
||
/* K M */
|
||
#define TRANSIENT_HEAP_TOTAL_SIZE (1024 * 1024 * 16)
|
||
#define TRANSIENT_HEAP_BLOCK_SIZE (1024 * 32 ) /* int16_t */
|
||
#define TRANSIENT_HEAP_ALLOC_MAX (1024 * 2 )
|
||
#define TRANSIENT_HEAP_BLOCK_NUM (TRANSIENT_HEAP_TOTAL_SIZE / TRANSIENT_HEAP_BLOCK_SIZE)
|
||
|
||
#define TRANSIENT_HEAP_ALLOC_MAGIC 0xfeab
|
||
#define TRANSIENT_HEAP_ALLOC_ALIGN RUBY_ALIGNOF(void *)
|
||
|
||
#define TRANSIENT_HEAP_ALLOC_MARKING_LAST -1
|
||
#define TRANSIENT_HEAP_ALLOC_MARKING_FREE -2
|
||
|
||
#define ROUND_UP(v, a) (((size_t)(v) + (a) - 1) & ~((a) - 1))
|
||
|
||
static void
|
||
transient_heap_block_dump(struct transient_heap* theap, struct transient_heap_block *block)
|
||
{
|
||
int i=0, n=0;
|
||
struct transient_alloc_header *header = NULL;
|
||
|
||
while (i<block->info.index) {
|
||
header = (void *)&block->buff[i];
|
||
fprintf(stderr, "%4d %8d %p size:%4d next:%4d %s\n", n, i, header, header->size, header->next_marked_index, rb_obj_info(header->obj));
|
||
i += header->size;
|
||
n++;
|
||
}
|
||
}
|
||
|
||
static void
|
||
transient_heap_blocks_dump(struct transient_heap* theap, struct transient_heap_block *block, const char *type_str)
|
||
{
|
||
while (block) {
|
||
fprintf(stderr, "- transient_heap_dump: %s:%p index:%d objects:%d last_marked_index:%d next:%p\n",
|
||
type_str, block, block->info.index, block->info.objects, block->info.last_marked_index, block->info.next_block);
|
||
|
||
transient_heap_block_dump(theap, block);
|
||
block = block->info.next_block;
|
||
}
|
||
}
|
||
|
||
static void
|
||
transient_heap_dump(struct transient_heap* theap)
|
||
{
|
||
fprintf(stderr, "transient_heap_dump objects:%d marked_objects:%d blocks:%d\n", theap->total_objects, theap->total_marked_objects, theap->total_blocks);
|
||
transient_heap_blocks_dump(theap, theap->using_blocks, "using_blocks");
|
||
transient_heap_blocks_dump(theap, theap->marked_blocks, "marked_blocks");
|
||
transient_heap_blocks_dump(theap, theap->free_blocks, "free_blocks");
|
||
}
|
||
|
||
void
|
||
rb_transient_heap_dump(void)
|
||
{
|
||
transient_heap_dump(&global_transient_heap);
|
||
}
|
||
|
||
#if TRANSIENT_HEAP_CHECK_MODE >= 2
|
||
static int
|
||
transient_heap_block_verify(struct transient_heap *theap, struct transient_heap_block *block)
|
||
{
|
||
int i=0, n=0;
|
||
struct transient_alloc_header *header;
|
||
|
||
while (i<block->info.index) {
|
||
header = (void *)&block->buff[i];
|
||
TH_ASSERT(header->magic == TRANSIENT_HEAP_ALLOC_MAGIC);
|
||
n ++;
|
||
i += header->size;
|
||
}
|
||
TH_ASSERT(block->info.objects == n);
|
||
|
||
return n;
|
||
}
|
||
#endif
|
||
|
||
static void
|
||
transient_heap_verify(struct transient_heap *theap)
|
||
{
|
||
#if TRANSIENT_HEAP_CHECK_MODE >= 2
|
||
struct transient_heap_block *block;
|
||
int n=0;
|
||
|
||
// using_blocks
|
||
block = theap->using_blocks;
|
||
while (block) {
|
||
n += transient_heap_block_verify(theap, block);
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
// marked_blocks
|
||
block = theap->marked_blocks;
|
||
while (block) {
|
||
n += transient_heap_block_verify(theap, block);
|
||
TH_ASSERT(block->info.index > 0);
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
TH_ASSERT(n == theap->total_objects);
|
||
TH_ASSERT(n >= theap->total_marked_objects);
|
||
#endif
|
||
}
|
||
|
||
static struct transient_heap*
|
||
transient_heap_get(void)
|
||
{
|
||
struct transient_heap* theap = &global_transient_heap;
|
||
transient_heap_verify(theap);
|
||
return theap;
|
||
}
|
||
|
||
static void
|
||
reset_block(struct transient_heap_block *block)
|
||
{
|
||
block->info.size = TRANSIENT_HEAP_BLOCK_SIZE - sizeof(struct transient_heap_block_header);
|
||
block->info.index = 0;
|
||
block->info.objects = 0;
|
||
block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
|
||
block->info.next_block = NULL;
|
||
}
|
||
|
||
static void
|
||
connect_to_free_blocks(struct transient_heap *theap, struct transient_heap_block *block)
|
||
{
|
||
block->info.next_block = theap->free_blocks;
|
||
theap->free_blocks = block;
|
||
}
|
||
|
||
static void
|
||
connect_to_using_blocks(struct transient_heap *theap, struct transient_heap_block *block)
|
||
{
|
||
block->info.next_block = theap->using_blocks;
|
||
theap->using_blocks = block;
|
||
}
|
||
|
||
#if 0
|
||
static void
|
||
connect_to_marked_blocks(struct transient_heap *theap, struct transient_heap_block *block)
|
||
{
|
||
block->info.next_block = theap->marked_blocks;
|
||
theap->marked_blocks = block;
|
||
}
|
||
#endif
|
||
|
||
static void
|
||
append_to_marked_blocks(struct transient_heap *theap, struct transient_heap_block *append_blocks)
|
||
{
|
||
if (theap->marked_blocks) {
|
||
struct transient_heap_block *block = theap->marked_blocks, *last_block = NULL;
|
||
while (block) {
|
||
last_block = block;
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
TH_ASSERT(last_block->info.next_block == NULL);
|
||
last_block->info.next_block = append_blocks;
|
||
}
|
||
else {
|
||
theap->marked_blocks = append_blocks;
|
||
}
|
||
}
|
||
|
||
static struct transient_heap_block *
|
||
transient_heap_block_alloc(struct transient_heap* theap)
|
||
{
|
||
struct transient_heap_block *block;
|
||
block = mmap(NULL, TRANSIENT_HEAP_BLOCK_SIZE, PROT_READ | PROT_WRITE,
|
||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||
-1, 0);
|
||
if (block == MAP_FAILED) rb_bug("transient_heap_block_alloc: err:%d\n", errno);
|
||
|
||
reset_block(block);
|
||
|
||
TH_ASSERT(((intptr_t)block->buff & (TRANSIENT_HEAP_ALLOC_ALIGN-1)) == 0);
|
||
|
||
theap->total_blocks++;
|
||
// fprintf(stderr, "transient_heap_block_alloc: %d\n", theap->total_blocks);
|
||
return block;
|
||
}
|
||
|
||
static struct transient_heap_block *
|
||
transient_heap_allocatable_block(struct transient_heap* theap)
|
||
{
|
||
struct transient_heap_block *block;
|
||
|
||
#if TRANSIENT_HEAP_INFINITE_BLOCK_MODE
|
||
block = transient_heap_block_alloc(theap);
|
||
#else
|
||
// get one block from free_blocks
|
||
block = theap->free_blocks;
|
||
if (block) {
|
||
theap->free_blocks = block->info.next_block;
|
||
block->info.next_block = NULL;
|
||
}
|
||
#endif
|
||
|
||
return block;
|
||
}
|
||
|
||
static struct transient_alloc_header *
|
||
transient_heap_allocatable_header(struct transient_heap* theap, size_t size)
|
||
{
|
||
struct transient_heap_block *block = theap->using_blocks;
|
||
|
||
while (block) {
|
||
TH_ASSERT(block->info.size >= block->info.index);
|
||
|
||
if (block->info.size - block->info.index >= (int32_t)size) {
|
||
struct transient_alloc_header *header = (void *)&block->buff[block->info.index];
|
||
block->info.index += size;
|
||
block->info.objects++;
|
||
return header;
|
||
}
|
||
else {
|
||
block = transient_heap_allocatable_block(theap);
|
||
if (block) connect_to_using_blocks(theap, block);
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
void *
|
||
rb_transient_heap_alloc(VALUE obj, size_t req_size)
|
||
{
|
||
struct transient_heap* theap = transient_heap_get();
|
||
size_t size = ROUND_UP(req_size + sizeof(struct transient_alloc_header), TRANSIENT_HEAP_ALLOC_ALIGN);
|
||
|
||
if (size > TRANSIENT_HEAP_ALLOC_MAX) {
|
||
// fprintf(stderr, "rb_transient_heap_alloc: NULL (too big: %ld)\n", (long)size);
|
||
return NULL;
|
||
}
|
||
else if (RB_OBJ_PROMOTED_RAW(obj)) {
|
||
// fprintf(stderr, "rb_transient_heap_alloc: NULL (not for promoted objects)\n");
|
||
return NULL;
|
||
}
|
||
else {
|
||
struct transient_alloc_header *header = transient_heap_allocatable_header(theap, size);
|
||
if (header) {
|
||
void *ptr;
|
||
|
||
header->size = size;
|
||
header->magic = TRANSIENT_HEAP_ALLOC_MAGIC;
|
||
header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
|
||
header->obj = obj; // TODO: for verify
|
||
|
||
// stat info
|
||
theap->total_objects++;
|
||
ptr = header + 1;
|
||
// fprintf(stderr, "rb_transient_heap_alloc: header:%p ptr:%p size:%d obj:%s\n", header, ptr, (int)size, rb_obj_info(obj));
|
||
return ptr;
|
||
}
|
||
else {
|
||
// fprintf(stderr, "rb_transient_heap_alloc: NULL (no enough space: %ld)\n", (long)size);
|
||
return NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
Init_TransientHeap(void)
|
||
{
|
||
int i, block_num;
|
||
struct transient_heap* theap = transient_heap_get();
|
||
|
||
#if TRANSIENT_HEAP_INFINITE_BLOCK_MODE
|
||
block_num = 1;
|
||
#else
|
||
TH_ASSERT(TRANSIENT_HEAP_BLOCK_SIZE * TRANSIENT_HEAP_BLOCK_NUM == TRANSIENT_HEAP_TOTAL_SIZE);
|
||
block_num = TRANSIENT_HEAP_BLOCK_NUM;
|
||
#endif
|
||
for (i=0; i<block_num-1; i++) {
|
||
connect_to_free_blocks(theap, transient_heap_block_alloc(theap));
|
||
}
|
||
theap->using_blocks = transient_heap_block_alloc(theap);
|
||
|
||
theap->promoted_objects_size = TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE;
|
||
theap->promoted_objects_index = 0;
|
||
/* should not use ALLOC_N to be free from GC */
|
||
theap->promoted_objects = malloc(sizeof(VALUE) * theap->promoted_objects_size);
|
||
if (theap->promoted_objects == NULL) rb_bug("Init_TransientHeap: malloc failed.");
|
||
}
|
||
|
||
static struct transient_heap_block *
|
||
blocks_alloc_header_to_block(struct transient_heap *theap, struct transient_heap_block *blocks, struct transient_alloc_header *header)
|
||
{
|
||
struct transient_heap_block *block = blocks;
|
||
|
||
TH_ASSERT(theap->status == transient_heap_marking);
|
||
|
||
while (block) {
|
||
if (block->buff <= (char *)header && (char *)header < block->buff + block->info.size) {
|
||
return block;
|
||
}
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static struct transient_heap_block *
|
||
alloc_header_to_block(struct transient_heap *theap, struct transient_alloc_header *header)
|
||
{
|
||
struct transient_heap_block *block;
|
||
|
||
if ((block = blocks_alloc_header_to_block(theap, theap->marked_blocks, header)) != NULL) {
|
||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "alloc_header_to_block: found in marked_blocks\n");
|
||
return block;
|
||
}
|
||
else if ((block = blocks_alloc_header_to_block(theap, theap->using_blocks, header)) != NULL) {
|
||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "alloc_header_to_block: found in using_blocks\n");
|
||
return block;
|
||
}
|
||
else {
|
||
transient_heap_dump(theap);
|
||
rb_bug("alloc_header_to_block: not found in mark_blocks (%p)\n", header);
|
||
}
|
||
}
|
||
|
||
void
|
||
rb_transient_heap_mark(VALUE obj, const void *ptr)
|
||
{
|
||
struct transient_alloc_header *header = (void *)ptr;
|
||
header = header - 1;
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_mark: %s (%p)\n", rb_obj_info(obj), ptr);
|
||
|
||
if (header->next_marked_index != TRANSIENT_HEAP_ALLOC_MARKING_FREE) {
|
||
// already marked
|
||
return;
|
||
}
|
||
|
||
if (header->magic != TRANSIENT_HEAP_ALLOC_MAGIC) {
|
||
struct transient_heap* theap = transient_heap_get();
|
||
transient_heap_dump(theap);
|
||
rb_bug("rb_transient_heap_mark: magic is broken");
|
||
}
|
||
else if (header->obj != obj) {
|
||
struct transient_heap* theap = transient_heap_get();
|
||
transient_heap_dump(theap);
|
||
rb_bug("rb_transient_heap_mark: unmatch\n");
|
||
}
|
||
else {
|
||
struct transient_heap* theap = transient_heap_get();
|
||
struct transient_heap_block *block = alloc_header_to_block(theap, header);
|
||
header->next_marked_index = block->info.last_marked_index;
|
||
block->info.last_marked_index = (int)((char *)header - block->buff);
|
||
theap->total_marked_objects++;
|
||
}
|
||
}
|
||
|
||
static void *
|
||
transient_heap_ptr(VALUE obj, int error)
|
||
{
|
||
void *ptr;
|
||
|
||
switch (BUILTIN_TYPE(obj)) {
|
||
case T_ARRAY:
|
||
if (ARY_TRANSIENT_P(obj)) {
|
||
ptr = (VALUE *)RARRAY_CONST_PTR(obj);
|
||
}
|
||
else {
|
||
ptr = NULL;
|
||
}
|
||
break;
|
||
default:
|
||
if (error) {
|
||
rb_bug("transient_heap_ptr: unknown obj %s\n", rb_obj_info(obj));
|
||
}
|
||
else {
|
||
ptr = NULL;
|
||
}
|
||
}
|
||
|
||
return ptr;
|
||
}
|
||
|
||
void
|
||
rb_transient_heap_promote(VALUE obj)
|
||
{
|
||
|
||
if (transient_heap_ptr(obj, FALSE)) {
|
||
struct transient_heap* theap = transient_heap_get();
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_promote: %s\n", rb_obj_info(obj));
|
||
|
||
if (theap->promoted_objects_size <= theap->promoted_objects_index) {
|
||
theap->promoted_objects_size *= 2;
|
||
if (TRANSIENT_HEAP_DEBUG >= 0) fprintf(stderr, "rb_transient_heap_promote: expand table to %d\n", theap->promoted_objects_size);
|
||
theap->promoted_objects = realloc(theap->promoted_objects, theap->promoted_objects_size * sizeof(VALUE));
|
||
if (theap->promoted_objects == NULL) rb_bug("rb_transient_heap_promote: realloc failed");
|
||
}
|
||
theap->promoted_objects[theap->promoted_objects_index++] = obj;
|
||
}
|
||
else {
|
||
/* ignore */
|
||
}
|
||
}
|
||
|
||
void
|
||
rb_transient_heap_promoted(VALUE obj, const void *ptr)
|
||
{
|
||
struct transient_alloc_header *header = (void *)ptr;
|
||
header = header - 1;
|
||
|
||
|
||
}
|
||
|
||
static struct transient_alloc_header *
|
||
alloc_header(struct transient_heap_block* block, int index)
|
||
{
|
||
return (void *)&block->buff[index];
|
||
}
|
||
|
||
void rb_ary_detransient(VALUE ary);
|
||
|
||
static void
|
||
transient_heap_reset(void)
|
||
{
|
||
struct transient_heap* theap = transient_heap_get();
|
||
struct transient_heap_block* block;
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_reset\n");
|
||
|
||
block = theap->marked_blocks;
|
||
while (block) {
|
||
struct transient_heap_block *next_block = block->info.next_block;
|
||
theap->total_objects -= block->info.objects;
|
||
#if TRANSIENT_HEAP_INFINITE_BLOCK_MODE
|
||
// debug mode
|
||
if (madvise(block, TRANSIENT_HEAP_BLOCK_SIZE, MADV_DONTNEED) != 0) {
|
||
rb_bug("madvise err:%d", errno);
|
||
}
|
||
if (mprotect(block, TRANSIENT_HEAP_BLOCK_SIZE, PROT_NONE) != 0) {
|
||
rb_bug("mprotect err:%d", errno);
|
||
}
|
||
theap->total_blocks--;
|
||
#else
|
||
reset_block(block);
|
||
connect_to_free_blocks(theap, block);
|
||
#endif
|
||
block = next_block;
|
||
}
|
||
|
||
theap->marked_blocks = NULL;
|
||
theap->total_marked_objects = 0;
|
||
}
|
||
|
||
static void
|
||
transient_heap_block_escape(struct transient_heap* theap, struct transient_heap_block* block)
|
||
{
|
||
int marked_index = block->info.last_marked_index;
|
||
block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
|
||
|
||
while (marked_index >= 0) {
|
||
struct transient_alloc_header *header = alloc_header(block, marked_index);
|
||
VALUE obj = header->obj;
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, " * transient_heap_block_escape %p %s\n", header, rb_obj_info(obj));
|
||
|
||
if (obj != Qnil) {
|
||
switch (BUILTIN_TYPE(obj)) {
|
||
case T_ARRAY:
|
||
rb_ary_detransient(obj);
|
||
break;
|
||
default:
|
||
rb_bug("unsupporeted");
|
||
}
|
||
header->obj = Qundef; // to verify
|
||
}
|
||
marked_index = header->next_marked_index;
|
||
}
|
||
}
|
||
|
||
static void
|
||
transient_heap_update_status(struct transient_heap* theap, enum transient_heap_status status)
|
||
{
|
||
TH_ASSERT(theap->status != status);
|
||
theap->status = status;
|
||
}
|
||
|
||
static void
|
||
transient_heap_escape(void *dmy)
|
||
{
|
||
struct transient_heap* theap = transient_heap_get();
|
||
|
||
if (theap->status == transient_heap_marking) {
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_escape: skip while transient_heap_marking\n");
|
||
}
|
||
else {
|
||
VALUE gc_disabled = rb_gc_disable();
|
||
struct transient_heap_block* block;
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_escape start total_blocks:%d\n", theap->total_blocks);
|
||
if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
|
||
|
||
TH_ASSERT(theap->status == transient_heap_none);
|
||
transient_heap_update_status(theap, transient_heap_escaping);
|
||
|
||
// escape marked blocks
|
||
block = theap->marked_blocks;
|
||
while (block) {
|
||
transient_heap_block_escape(theap, block);
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
// escape using blocks
|
||
// only affect incremental marking
|
||
block = theap->using_blocks;
|
||
while (block) {
|
||
transient_heap_block_escape(theap, block);
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
// all objects in marked_objects are escaped.
|
||
transient_heap_reset();
|
||
|
||
if (TRANSIENT_HEAP_DEBUG > 0) {
|
||
fprintf(stderr, "!! transient_heap_escape end total_blocks:%d\n", theap->total_blocks);
|
||
// transient_heap_dump(theap);
|
||
}
|
||
|
||
transient_heap_verify(theap);
|
||
transient_heap_update_status(theap, transient_heap_none);
|
||
if (gc_disabled != Qtrue) rb_gc_enable();
|
||
}
|
||
}
|
||
|
||
static void
|
||
clear_marked_index(struct transient_heap_block* block)
|
||
{
|
||
int marked_index = block->info.last_marked_index;
|
||
|
||
while (marked_index != TRANSIENT_HEAP_ALLOC_MARKING_LAST) {
|
||
struct transient_alloc_header *header = alloc_header(block, marked_index);
|
||
TH_ASSERT(marked_index != TRANSIENT_HEAP_ALLOC_MARKING_FREE);
|
||
// fprintf(stderr, "clear_marked_index - block:%p mark_index:%d\n", block, marked_index);
|
||
|
||
marked_index = header->next_marked_index;
|
||
header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
|
||
}
|
||
|
||
block->info.last_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_LAST;
|
||
}
|
||
|
||
void
|
||
rb_transient_heap_start_marking(int full_marking)
|
||
{
|
||
struct transient_heap* theap = transient_heap_get();
|
||
struct transient_heap_block* block;
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! rb_transient_heap_start_marking objects:%d blocks:%d full_marking:%d\n",
|
||
theap->total_objects, theap->total_blocks, full_marking);
|
||
if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
|
||
|
||
// clear marking info
|
||
block = theap->marked_blocks;
|
||
while (block) {
|
||
clear_marked_index(block);
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
block = theap->using_blocks;
|
||
while (block) {
|
||
clear_marked_index(block);
|
||
block = block->info.next_block;
|
||
}
|
||
|
||
if (theap->using_blocks) {
|
||
if (theap->using_blocks->info.objects > 0) {
|
||
append_to_marked_blocks(theap, theap->using_blocks);
|
||
theap->using_blocks = NULL;
|
||
}
|
||
else {
|
||
append_to_marked_blocks(theap, theap->using_blocks->info.next_block);
|
||
theap->using_blocks->info.next_block = NULL;
|
||
}
|
||
}
|
||
|
||
if (theap->using_blocks == NULL) {
|
||
theap->using_blocks = transient_heap_allocatable_block(theap);
|
||
}
|
||
|
||
TH_ASSERT(theap->status == transient_heap_none);
|
||
transient_heap_update_status(theap, transient_heap_marking);
|
||
theap->total_marked_objects = 0;
|
||
|
||
if (full_marking) {
|
||
theap->promoted_objects_index = 0;
|
||
}
|
||
else { /* mark promoted objects */
|
||
int i;
|
||
for (i=0; i<theap->promoted_objects_index; i++) {
|
||
VALUE obj = theap->promoted_objects[i];
|
||
void *ptr = transient_heap_ptr(obj, TRUE);
|
||
if (ptr) {
|
||
rb_transient_heap_mark(obj, ptr);
|
||
}
|
||
}
|
||
}
|
||
|
||
transient_heap_verify(theap);
|
||
}
|
||
|
||
void
|
||
rb_transient_heap_finish_marking(void)
|
||
{
|
||
struct transient_heap* theap = transient_heap_get();
|
||
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! rb_transient_heap_finish_marking objects:%d, marked:%d\n",
|
||
theap->total_objects,
|
||
theap->total_marked_objects);
|
||
if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
|
||
|
||
TH_ASSERT(theap->total_objects >= theap->total_marked_objects);
|
||
|
||
TH_ASSERT(theap->status == transient_heap_marking);
|
||
transient_heap_update_status(theap, transient_heap_none);
|
||
|
||
if (theap->total_marked_objects > 0) {
|
||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "-> rb_transient_heap_finish_marking register escape func.\n");
|
||
rb_postponed_job_register_one(0, transient_heap_escape, NULL);
|
||
}
|
||
else {
|
||
transient_heap_reset();
|
||
}
|
||
|
||
transient_heap_verify(theap);
|
||
}
|
||