1/* -*-c-*- */
2/*
3 * from eval.c
4 */
5
6#include "eval_intern.h"
7
8/* exit */
9
10void
11rb_call_end_proc(VALUE data)
12{
13    rb_proc_call(data, rb_ary_new());
14}
15
16/*
17 *  call-seq:
18 *     at_exit { block } -> proc
19 *
20 *  Converts _block_ to a +Proc+ object (and therefore
21 *  binds it at the point of call) and registers it for execution when
22 *  the program exits. If multiple handlers are registered, they are
23 *  executed in reverse order of registration.
24 *
25 *     def do_at_exit(str1)
26 *       at_exit { print str1 }
27 *     end
28 *     at_exit { puts "cruel world" }
29 *     do_at_exit("goodbye ")
30 *     exit
31 *
32 *  <em>produces:</em>
33 *
34 *     goodbye cruel world
35 */
36
37static VALUE
38rb_f_at_exit(void)
39{
40    VALUE proc;
41
42    if (!rb_block_given_p()) {
43	rb_raise(rb_eArgError, "called without a block");
44    }
45    proc = rb_block_proc();
46    rb_set_end_proc(rb_call_end_proc, proc);
47    return proc;
48}
49
50struct end_proc_data {
51    void (*func) ();
52    VALUE data;
53    int safe;
54    struct end_proc_data *next;
55};
56
57static struct end_proc_data *end_procs, *ephemeral_end_procs;
58
59void
60rb_set_end_proc(void (*func)(VALUE), VALUE data)
61{
62    struct end_proc_data *link = ALLOC(struct end_proc_data);
63    struct end_proc_data **list;
64    rb_thread_t *th = GET_THREAD();
65
66    if (th->top_wrapper) {
67	list = &ephemeral_end_procs;
68    }
69    else {
70	list = &end_procs;
71    }
72    link->next = *list;
73    link->func = func;
74    link->data = data;
75    link->safe = rb_safe_level();
76    *list = link;
77}
78
79void
80rb_mark_end_proc(void)
81{
82    struct end_proc_data *link;
83
84    link = end_procs;
85    while (link) {
86	rb_gc_mark(link->data);
87	link = link->next;
88    }
89    link = ephemeral_end_procs;
90    while (link) {
91	rb_gc_mark(link->data);
92	link = link->next;
93    }
94}
95
96void
97rb_exec_end_proc(void)
98{
99    struct end_proc_data volatile endproc;
100    struct end_proc_data volatile *link;
101    int status;
102    volatile int safe = rb_safe_level();
103    rb_thread_t *th = GET_THREAD();
104    volatile VALUE errinfo = th->errinfo;
105
106    while (ephemeral_end_procs) {
107	link = ephemeral_end_procs;
108	ephemeral_end_procs = link->next;
109	endproc = *link;
110	xfree((void *)link);
111	link = &endproc;
112
113	PUSH_TAG();
114	if ((status = EXEC_TAG()) == 0) {
115	    rb_set_safe_level_force(link->safe);
116	    (*link->func) (link->data);
117	}
118	POP_TAG();
119	if (status) {
120	    error_handle(status);
121	    if (!NIL_P(th->errinfo)) errinfo = th->errinfo;
122	}
123    }
124
125    while (end_procs) {
126	link = end_procs;
127	end_procs = link->next;
128	endproc = *link;
129	xfree((void *)link);
130	link = &endproc;
131
132	PUSH_TAG();
133	if ((status = EXEC_TAG()) == 0) {
134	    rb_set_safe_level_force(link->safe);
135	    (*link->func) (link->data);
136	}
137	POP_TAG();
138	if (status) {
139	    error_handle(status);
140	    if (!NIL_P(th->errinfo)) errinfo = th->errinfo;
141	}
142    }
143
144    rb_set_safe_level_force(safe);
145    th->errinfo = errinfo;
146}
147
148void
149Init_jump(void)
150{
151    rb_define_global_function("at_exit", rb_f_at_exit, 0);
152}
153