diff options
Diffstat (limited to 'eval_jump.c')
| -rw-r--r-- | eval_jump.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/eval_jump.c b/eval_jump.c new file mode 100644 index 0000000000..6ee8ff4a6f --- /dev/null +++ b/eval_jump.c @@ -0,0 +1,138 @@ +/* -*-c-*- */ +/* + * from eval.c + */ + +#include "eval_intern.h" + +/* exit */ + +void +rb_call_end_proc(VALUE data) +{ + rb_proc_call(data, rb_ary_new()); +} + +/* + * call-seq: + * at_exit { block } -> proc + * + * Converts _block_ to a +Proc+ object (and therefore + * binds it at the point of call) and registers it for execution when + * the program exits. If multiple handlers are registered, they are + * executed in reverse order of registration. + * + * def do_at_exit(str1) + * at_exit { print str1 } + * end + * at_exit { puts "cruel world" } + * do_at_exit("goodbye ") + * exit + * + * <em>produces:</em> + * + * goodbye cruel world + */ + +static VALUE +rb_f_at_exit(VALUE _) +{ + VALUE proc; + + if (!rb_block_given_p()) { + rb_raise(rb_eArgError, "called without a block"); + } + proc = rb_block_proc(); + rb_set_end_proc(rb_call_end_proc, proc); + return proc; +} + +struct end_proc_data { + void (*func) (VALUE); + VALUE data; + struct end_proc_data *next; +}; + +static struct end_proc_data *end_procs, *ephemeral_end_procs; + +void +rb_set_end_proc(void (*func)(VALUE), VALUE data) +{ + struct end_proc_data *link = ALLOC(struct end_proc_data); + struct end_proc_data **list; + rb_thread_t *th = GET_THREAD(); + + if (th->top_wrapper) { + list = &ephemeral_end_procs; + } + else { + list = &end_procs; + } + link->next = *list; + link->func = func; + link->data = data; + *list = link; +} + +void +rb_mark_end_proc(void) +{ + struct end_proc_data *link; + + link = end_procs; + while (link) { + rb_gc_mark(link->data); + link = link->next; + } + link = ephemeral_end_procs; + while (link) { + rb_gc_mark(link->data); + link = link->next; + } +} + +static void +exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp) +{ + struct end_proc_data volatile endproc; + struct end_proc_data *link; + VALUE errinfo = *errp; + + while ((link = *procs) != 0) { + *procs = link->next; + endproc = *link; + SIZED_FREE(link); + (*endproc.func) (endproc.data); + *errp = errinfo; + } +} + +static void +rb_ec_exec_end_proc(rb_execution_context_t * ec) +{ + enum ruby_tag_type state; + volatile VALUE errinfo = ec->errinfo; + volatile bool finished = false; + + while (!finished) { + EC_PUSH_TAG(ec); + if ((state = EC_EXEC_TAG()) == TAG_NONE) { + exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo); + exec_end_procs_chain(&end_procs, &ec->errinfo); + finished = true; + } + EC_POP_TAG(); + if (state != TAG_NONE) { + error_handle(ec, ec->errinfo, state); + if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo; + } + } + + ec->errinfo = errinfo; +} + +void +Init_jump(void) +{ + rb_define_global_function("at_exit", rb_f_at_exit, 0); +} |
