-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
iseq recycled while still in use #49
Comments
It is related to how Ruby compiles AST nodes. rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
{
DECL_ANCHOR(ret); // Linked list, not heap object. `ret` is a LINK_ANCHOR
INIT_ANCHOR(ret);
// ...
case ISEQ_TYPE_PLAIN:
CHECK(COMPILE(ret, "ensure", node)); // `Calls iseq_compile_each`
// ...
// If GC is triggered before returning, things in `ret` will be collected.
return iseq_setup(iseq, ret); // Move things from `ret` into `iseq`.
}
static int
iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
{
// ...
return iseq_compile_each0(iseq, ret, node, popped);
}
static int
iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
// ... many lines omitted
case NODE_DEFS:{
// ...
// Create an iseq
const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn,
rb_id2str(mid),
ISEQ_TYPE_METHOD, line);
// ...
// Add it into the linked list `ret`.
ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
// The following write barrier behaves as if `singleton_method_iseq` were a field of `iseq`,
// but the `ADD_INSN2` above actually writes `singleton_method_iseq` into a linked list.
// `singleton_method_iseq` is not yet reachable from `iseq`.
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
// ...
break;
}
// ... many lines omitted
} The function However, |
This problem does not exist in vanilla CRuby. While rb_iseq_t *
rb_iseq_new_with_opt(...)
{
// ...
prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1,
parent, isolated_depth, type, script_lines, option);
rb_iseq_compile_node(iseq, node);
finish_iseq_build(iseq);
// ...
}
static VALUE
prepare_iseq_build(rb_iseq_t *iseq, ...)
{
// ...
ISEQ_COMPILE_DATA(iseq)->insn.storage_head = ISEQ_COMPILE_DATA(iseq)->insn.storage_current = new_arena();
// ...
} The "arena" maintains contiguous regions allocated using If GC is triggered, void
rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
{
// ...
else if (FL_TEST_RAW((VALUE)iseq, ISEQ_USE_COMPILE_DATA)) {
const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
if (!reference_updating) {
/* The operands in each instruction needs to be pinned because
* if auto-compaction runs in iseq_set_sequence, then the objects
* could exist on the generated_iseq buffer, which would not be
* reference updated which can lead to T_MOVED (and subsequently
* T_NONE) objects on the iseq. */
rb_iseq_mark_and_pin_insn_storage(compile_data->insn.storage_head); // THIS!
}
rb_gc_mark_and_move((VALUE *)&compile_data->err_info);
rb_gc_mark_and_move((VALUE *)&compile_data->catch_table_ary);
}
// ...
} It turned out that all But this also means Unfortunately, mmtk-ruby doesn't handle this properly. Currently The obvious solution to this problem is making An alternative solution is taking advantage of the last-in-first-out nature of the construction of But a more interesting question should be why does CRuby allocate INSN instances in the arena instead of the MMTk heap. The Immix allocator is very efficient. |
I tried to maintain a stack of $stderr = STDOUT
5000.times do
begin
eval "0 rescue break"
rescue SyntaxError
end
end If exception is thrown, dead iseqs will remain on the stack. I'll try to remove elements on the top when popping if the top elements don't match the expected |
A commit in the `ruby` repo now treats iseq as PPP again, and fixes a random failure in CI. Fixes: mmtk#49
mmtk/ruby@68a6bbf is a one-line workaround to treat iseq as PPP again. But we need to look into the impact of treating iseq as PPP. iseq tends to be long-living. We may remove |
The Ruby binding tests failed in mmtk/mmtk-core#1073. The error doesn't seem to be related to that PR.
It is reproducible locally. The segmentation fault happens in
ImmixSpace::mark_line
because it attempted to access unmapped address. It is because the object size encoded in the hidden field added by the mmtk-ruby binding is overwritten to a very large number. This means it is a dangling reference bug.This happens in the very early stage of execution. It can be reproduced by executing an empty script (or a "hello world" program of course). One way to reproduce it is:
It is easier to reproduce when there are fewer GC threads. 2 is the best. 1 is still able to reproduce it.
Assertion fails because a traced object is already dead. The current evidence shows it is an
iseq
created duringbuiltin_iseq_load
, but is dead (but still referenced) inrb_iseq_eval
.The text was updated successfully, but these errors were encountered: