mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* gc.c: introduce RZombie to manage zombie objects.
Rewrite finalizing logics with this type. * gc.c (gc_verify_internal_consistency): verify zombie (finalizing) objects count. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@46348 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b165f9b5e2
commit
2f73dbd084
2 changed files with 110 additions and 67 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
Wed Jun 4 22:28:14 2014 Koichi Sasada <ko1@atdot.net>
|
||||||
|
|
||||||
|
* gc.c: introduce RZombie to manage zombie objects.
|
||||||
|
Rewrite finalizing logics with this type.
|
||||||
|
|
||||||
|
* gc.c (gc_verify_internal_consistency): verify zombie (finalizing)
|
||||||
|
objects count.
|
||||||
|
|
||||||
Wed Jun 4 22:09:53 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Wed Jun 4 22:09:53 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* re.c (match_aref, rb_reg_regsub): consider encoding of captured
|
* re.c (match_aref, rb_reg_regsub): consider encoding of captured
|
||||||
|
|
169
gc.c
169
gc.c
|
@ -454,7 +454,7 @@ typedef struct rb_objspace {
|
||||||
|
|
||||||
/* final */
|
/* final */
|
||||||
size_t final_slots;
|
size_t final_slots;
|
||||||
RVALUE *deferred_final;
|
VALUE deferred_final;
|
||||||
} heap_pages;
|
} heap_pages;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -653,6 +653,15 @@ VALUE *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
|
||||||
|
|
||||||
#define RANY(o) ((RVALUE*)(o))
|
#define RANY(o) ((RVALUE*)(o))
|
||||||
|
|
||||||
|
struct RZombie {
|
||||||
|
struct RBasic basic;
|
||||||
|
VALUE next;
|
||||||
|
void (*dfree)(void *);
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RZOMBIE(o) ((struct RZombie *)(o))
|
||||||
|
|
||||||
#define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
|
#define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
|
||||||
|
|
||||||
int ruby_gc_debug_indent = 0;
|
int ruby_gc_debug_indent = 0;
|
||||||
|
@ -1516,20 +1525,21 @@ rb_free_const_table(st_table *tbl)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
make_deferred(rb_objspace_t *objspace,RVALUE *p)
|
make_zombie(rb_objspace_t *objspace, VALUE obj, void (*dfree)(void *), void *data)
|
||||||
{
|
{
|
||||||
p->as.basic.flags = T_ZOMBIE;
|
struct RZombie *zombie = RZOMBIE(obj);
|
||||||
p->as.free.next = heap_pages_deferred_final;
|
zombie->basic.flags = T_ZOMBIE;
|
||||||
heap_pages_deferred_final = p;
|
zombie->dfree = dfree;
|
||||||
|
zombie->data = data;
|
||||||
|
zombie->next = heap_pages_deferred_final;
|
||||||
|
heap_pages_deferred_final = (VALUE)zombie;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
make_io_deferred(rb_objspace_t *objspace,RVALUE *p)
|
make_io_zombie(rb_objspace_t *objspace, VALUE obj)
|
||||||
{
|
{
|
||||||
rb_io_t *fptr = p->as.file.fptr;
|
rb_io_t *fptr = RANY(obj)->as.file.fptr;
|
||||||
make_deferred(objspace, p);
|
make_zombie(objspace, obj, (void (*)(void*))rb_io_fptr_finalize, fptr);
|
||||||
p->as.data.dfree = (void (*)(void*))rb_io_fptr_finalize;
|
|
||||||
p->as.data.data = fptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1612,22 +1622,30 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
|
||||||
case T_DATA:
|
case T_DATA:
|
||||||
if (DATA_PTR(obj)) {
|
if (DATA_PTR(obj)) {
|
||||||
int free_immediately = FALSE;
|
int free_immediately = FALSE;
|
||||||
|
void (*dfree)(void *);
|
||||||
|
void *data = DATA_PTR(obj);
|
||||||
|
|
||||||
if (RTYPEDDATA_P(obj)) {
|
if (RTYPEDDATA_P(obj)) {
|
||||||
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
|
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
|
||||||
RDATA(obj)->dfree = RANY(obj)->as.typeddata.type->function.dfree;
|
dfree = RANY(obj)->as.typeddata.type->function.dfree;
|
||||||
if (0 && free_immediately == 0) /* to expose non-free-immediate T_DATA */
|
if (0 && free_immediately == 0) {
|
||||||
|
/* to expose non-free-immediate T_DATA */
|
||||||
fprintf(stderr, "not immediate -> %s\n", RANY(obj)->as.typeddata.type->wrap_struct_name);
|
fprintf(stderr, "not immediate -> %s\n", RANY(obj)->as.typeddata.type->wrap_struct_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (RANY(obj)->as.data.dfree == RUBY_DEFAULT_FREE) {
|
else {
|
||||||
xfree(DATA_PTR(obj));
|
dfree = RANY(obj)->as.data.dfree;
|
||||||
}
|
}
|
||||||
else if (RANY(obj)->as.data.dfree) {
|
|
||||||
if (free_immediately) {
|
if (dfree) {
|
||||||
(RDATA(obj)->dfree)(DATA_PTR(obj));
|
if (dfree == RUBY_DEFAULT_FREE) {
|
||||||
|
xfree(data);
|
||||||
|
}
|
||||||
|
else if (free_immediately) {
|
||||||
|
(*dfree)(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
make_deferred(objspace, RANY(obj));
|
make_zombie(objspace, obj, dfree, data);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1644,7 +1662,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
|
||||||
break;
|
break;
|
||||||
case T_FILE:
|
case T_FILE:
|
||||||
if (RANY(obj)->as.file.fptr) {
|
if (RANY(obj)->as.file.fptr) {
|
||||||
make_io_deferred(objspace, RANY(obj));
|
make_io_zombie(objspace, obj);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1707,7 +1725,13 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
|
||||||
BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags);
|
BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (FL_TEST(obj, FL_FINALIZE)) {
|
||||||
|
make_zombie(objspace, obj, 0, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2108,56 +2132,48 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
run_final(rb_objspace_t *objspace, VALUE obj)
|
run_final(rb_objspace_t *objspace, VALUE zombie)
|
||||||
{
|
{
|
||||||
RUBY_DATA_FUNC free_func = 0;
|
|
||||||
st_data_t key, table;
|
st_data_t key, table;
|
||||||
|
|
||||||
heap_pages_final_slots--;
|
if (RZOMBIE(zombie)->dfree) {
|
||||||
|
RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data);
|
||||||
RBASIC_CLEAR_CLASS(obj);
|
|
||||||
|
|
||||||
if (RTYPEDDATA_P(obj)) {
|
|
||||||
free_func = RTYPEDDATA_TYPE(obj)->function.dfree;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
free_func = RDATA(obj)->dfree;
|
|
||||||
}
|
|
||||||
if (free_func) {
|
|
||||||
(*free_func)(DATA_PTR(obj));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
key = (st_data_t)obj;
|
key = (st_data_t)zombie;
|
||||||
if (st_delete(finalizer_table, &key, &table)) {
|
if (st_delete(finalizer_table, &key, &table)) {
|
||||||
run_finalizer(objspace, obj, (VALUE)table);
|
run_finalizer(objspace, zombie, (VALUE)table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
finalize_list(rb_objspace_t *objspace, RVALUE *p)
|
finalize_list(rb_objspace_t *objspace, VALUE zombie)
|
||||||
{
|
{
|
||||||
while (p) {
|
while (zombie) {
|
||||||
RVALUE *tmp = p->as.free.next;
|
VALUE next_zombie = RZOMBIE(zombie)->next;
|
||||||
struct heap_page *page = GET_HEAP_PAGE(p);
|
struct heap_page *page = GET_HEAP_PAGE(zombie);
|
||||||
|
|
||||||
run_final(objspace, (VALUE)p);
|
run_final(objspace, zombie);
|
||||||
|
|
||||||
|
RZOMBIE(zombie)->basic.flags = 0;
|
||||||
|
heap_pages_final_slots--;
|
||||||
|
page->final_slots--;
|
||||||
|
heap_page_add_freeobj(objspace, GET_HEAP_PAGE(zombie), zombie);
|
||||||
|
|
||||||
|
heap_pages_swept_slots++;
|
||||||
objspace->profile.total_freed_object_num++;
|
objspace->profile.total_freed_object_num++;
|
||||||
|
|
||||||
page->final_slots--;
|
zombie = next_zombie;
|
||||||
heap_page_add_freeobj(objspace, GET_HEAP_PAGE(p), (VALUE)p);
|
|
||||||
heap_pages_swept_slots++;
|
|
||||||
|
|
||||||
p = tmp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
finalize_deferred(rb_objspace_t *objspace)
|
finalize_deferred(rb_objspace_t *objspace)
|
||||||
{
|
{
|
||||||
RVALUE *p;
|
VALUE zombie;
|
||||||
|
|
||||||
while ((p = ATOMIC_PTR_EXCHANGE(heap_pages_deferred_final, 0)) != 0) {
|
while ((zombie = (VALUE)ATOMIC_PTR_EXCHANGE(heap_pages_deferred_final, 0)) != 0) {
|
||||||
finalize_list(objspace, p);
|
finalize_list(objspace, zombie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2261,12 +2277,12 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
|
||||||
xfree(DATA_PTR(p));
|
xfree(DATA_PTR(p));
|
||||||
}
|
}
|
||||||
else if (RANY(p)->as.data.dfree) {
|
else if (RANY(p)->as.data.dfree) {
|
||||||
make_deferred(objspace, RANY(p));
|
make_zombie(objspace, (VALUE)p, RANY(p)->as.data.dfree, RANY(p)->as.data.data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_FILE:
|
case T_FILE:
|
||||||
if (RANY(p)->as.file.fptr) {
|
if (RANY(p)->as.file.fptr) {
|
||||||
make_io_deferred(objspace, RANY(p));
|
make_io_zombie(objspace, (VALUE)p);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2591,7 +2607,7 @@ obj_memsize_of(VALUE obj, int use_tdata)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ZOMBIE:
|
case T_ZOMBIE:
|
||||||
break;
|
/* fall through */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
|
rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
|
||||||
|
@ -2804,11 +2820,6 @@ gc_page_sweep(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_
|
||||||
if (obj_free(objspace, (VALUE)p)) {
|
if (obj_free(objspace, (VALUE)p)) {
|
||||||
final_slots++;
|
final_slots++;
|
||||||
}
|
}
|
||||||
else if (FL_TEST(p, FL_FINALIZE)) {
|
|
||||||
RDATA(p)->dfree = 0;
|
|
||||||
make_deferred(objspace,p);
|
|
||||||
final_slots++;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
(void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, sizeof(RVALUE));
|
(void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, sizeof(RVALUE));
|
||||||
heap_page_add_freeobj(objspace, sweep_page, (VALUE)p);
|
heap_page_add_freeobj(objspace, sweep_page, (VALUE)p);
|
||||||
|
@ -2849,6 +2860,7 @@ gc_page_sweep(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_
|
||||||
sweep_page->free_next = NULL;
|
sweep_page->free_next = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_pages_swept_slots += freed_slots + empty_slots;
|
heap_pages_swept_slots += freed_slots + empty_slots;
|
||||||
objspace->profile.total_freed_object_num += freed_slots;
|
objspace->profile.total_freed_object_num += freed_slots;
|
||||||
heap_pages_final_slots += final_slots;
|
heap_pages_final_slots += final_slots;
|
||||||
|
@ -3053,7 +3065,7 @@ gc_after_sweep(rb_objspace_t *objspace)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if RGENGC_CHECK_MODE >= 2
|
#if RGENGC_CHECK_MODE >= 2
|
||||||
gc_verify_internal_consistency(Qnil);
|
gc_verify_internal_consistency(Qnil);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_SWEEP, 0);
|
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_SWEEP, 0);
|
||||||
|
@ -4506,6 +4518,7 @@ struct verify_internal_consistency_struct {
|
||||||
#if RGENGC_AGE2_PROMOTION
|
#if RGENGC_AGE2_PROMOTION
|
||||||
size_t young_object_count;
|
size_t young_object_count;
|
||||||
#endif
|
#endif
|
||||||
|
size_t zombie_object_count;
|
||||||
VALUE parent;
|
VALUE parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4546,13 +4559,18 @@ verify_internal_consistency_i(void *page_start, void *page_end, size_t stride, v
|
||||||
data->young_object_count++;
|
data->young_object_count++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (RVALUE_OLD_P(v)) {
|
if (RVALUE_OLD_P(v)) {
|
||||||
data->parent = v;
|
data->parent = v;
|
||||||
/* reachable objects from an oldgen object should be old or (young with remember) */
|
/* reachable objects from an oldgen object should be old or (young with remember) */
|
||||||
rb_objspace_reachable_objects_from(v, verify_internal_consistency_reachable_i, (void *)data);
|
rb_objspace_reachable_objects_from(v, verify_internal_consistency_reachable_i, (void *)data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (BUILTIN_TYPE(v) == T_ZOMBIE) {
|
||||||
|
assert(RBASIC(v)->flags == T_ZOMBIE);
|
||||||
|
data->zombie_object_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4574,16 +4592,9 @@ static VALUE
|
||||||
gc_verify_internal_consistency(VALUE self)
|
gc_verify_internal_consistency(VALUE self)
|
||||||
{
|
{
|
||||||
#if USE_RGENGC
|
#if USE_RGENGC
|
||||||
struct verify_internal_consistency_struct data;
|
struct verify_internal_consistency_struct data = {0};
|
||||||
rb_objspace_t *objspace = &rb_objspace;
|
rb_objspace_t *objspace = &rb_objspace;
|
||||||
data.objspace = objspace;
|
data.objspace = objspace;
|
||||||
data.err_count = 0;
|
|
||||||
|
|
||||||
data.live_object_count = 0;
|
|
||||||
data.old_object_count = 0;
|
|
||||||
#if RGENGC_AGE2_PROMOTION
|
|
||||||
data.young_object_count = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{
|
{
|
||||||
struct each_obj_args eo_args;
|
struct each_obj_args eo_args;
|
||||||
|
@ -4618,6 +4629,30 @@ gc_verify_internal_consistency(VALUE self)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!finalizing) {
|
||||||
|
size_t list_count = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
VALUE z = heap_pages_deferred_final;
|
||||||
|
while (z) {
|
||||||
|
list_count++;
|
||||||
|
z = RZOMBIE(z)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heap_pages_final_slots != data.zombie_object_count ||
|
||||||
|
heap_pages_final_slots != list_count) {
|
||||||
|
|
||||||
|
rb_bug("inconsistent finalizing object count:\n"
|
||||||
|
" expect %"PRIuSIZE"\n"
|
||||||
|
" but %"PRIuSIZE" zombies\n"
|
||||||
|
" heap_pages_deferred_final list has %"PRIuSIZE" items.",
|
||||||
|
heap_pages_final_slots,
|
||||||
|
data.zombie_object_count,
|
||||||
|
list_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue