diff options
Diffstat (limited to 'eval_jump.c')
-rw-r--r-- | eval_jump.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/eval_jump.c b/eval_jump.c new file mode 100644 index 0000000..a8f0480 --- /dev/null +++ b/eval_jump.c @@ -0,0 +1,141 @@ +/* -*-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(void) +{ + 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 data; + int safe; + 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; + link->safe = rb_safe_level(); + *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; + } +} + +void +rb_exec_end_proc(void) +{ + struct end_proc_data *volatile link; + int status; + volatile int safe = rb_safe_level(); + + while (ephemeral_end_procs) { + link = ephemeral_end_procs; + ephemeral_end_procs = link->next; + + PUSH_TAG(); + if ((status = EXEC_TAG()) == 0) { + rb_set_safe_level_force(link->safe); + (*link->func) (link->data); + } + POP_TAG(); + if (status) { + error_handle(status); + } + xfree(link); + } + + while (end_procs) { + link = end_procs; + end_procs = link->next; + + PUSH_TAG(); + if ((status = EXEC_TAG()) == 0) { + rb_set_safe_level_force(link->safe); + (*link->func) (link->data); + } + POP_TAG(); + if (status) { + error_handle(status); + } + xfree(link); + } + rb_set_safe_level_force(safe); +} + +void +Init_jump(void) +{ + rb_define_global_function("at_exit", rb_f_at_exit, 0); +} |