/* -*-c-*- */ /* * from eval.c */ #include "eval_intern.h" #include "internal/ractor.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 * * produces: * * 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"); } rb_ractor_ensure_main_ractor("can not call at_exit from non-main Ractors"); 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; /* Share only among END/at_exit handlers to avoid repeated causes. */ VALUE shown_causes = 0; 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) { VALUE err = ec->errinfo; add_shown_cause(errinfo, &shown_causes); error_handle_with_shown_causes(ec, err, state, &shown_causes); if (!NIL_P(err)) errinfo = err; } } ec->errinfo = errinfo; } void Init_jump(void) { rb_define_global_function("at_exit", rb_f_at_exit, 0); }