Merge commit '29776e85da637ec4d44b2b2822d6934a50c0084b' into boehm-demers-weiser-gc
[bpt/guile.git] / libguile / dynwind.c
CommitLineData
2b829bbb 1/* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004, 2006 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e
MV
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
0f2d19dd 7 *
73be1d9e
MV
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
0f2d19dd 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
92205699 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
73be1d9e 16 */
1bbd0b84 17
1bbd0b84 18
0f2d19dd
JB
19\f
20
4845bbae
MV
21#include <assert.h>
22
a0599745
MD
23#include "libguile/_scm.h"
24#include "libguile/eval.h"
25#include "libguile/alist.h"
26#include "libguile/fluids.h"
27#include "libguile/ports.h"
28#include "libguile/smob.h"
0f2d19dd 29
a0599745 30#include "libguile/dynwind.h"
0f2d19dd
JB
31\f
32
33/* {Dynamic wind}
b3460a50
MV
34
35 Things that can be on the wind list:
36
4845bbae
MV
37 #<frame>
38 #<winder>
b3460a50
MV
39 (enter-proc . leave-proc) dynamic-wind
40 (tag . jmpbuf) catch
43e01b1e 41 (tag . pre-unwind-data) throw-handler / lazy-catch
b3460a50
MV
42 tag is either a symbol or a boolean
43
b3460a50 44*/
0f2d19dd
JB
45
46
47
3b3b36dd 48SCM_DEFINE (scm_dynamic_wind, "dynamic-wind", 3, 0, 0,
1e6808ea
MG
49 (SCM in_guard, SCM thunk, SCM out_guard),
50 "All three arguments must be 0-argument procedures.\n"
51 "@var{in_guard} is called, then @var{thunk}, then\n"
52 "@var{out_guard}.\n"
53 "\n"
54 "If, any time during the execution of @var{thunk}, the\n"
55 "continuation of the @code{dynamic_wind} expression is escaped\n"
56 "non-locally, @var{out_guard} is called. If the continuation of\n"
57 "the dynamic-wind is re-entered, @var{in_guard} is called. Thus\n"
58 "@var{in_guard} and @var{out_guard} may be called any number of\n"
59 "times.\n"
60 "@lisp\n"
b380b885 61 "(define x 'normal-binding)\n"
1e6808ea 62 "@result{} x\n"
9401323e 63 "(define a-cont (call-with-current-continuation\n"
b380b885
MD
64 " (lambda (escape)\n"
65 " (let ((old-x x))\n"
66 " (dynamic-wind\n"
67 " ;; in-guard:\n"
68 " ;;\n"
1e6808ea
MG
69 " (lambda () (set! x 'special-binding))\n"
70 "\n"
b380b885
MD
71 " ;; thunk\n"
72 " ;;\n"
73 " (lambda () (display x) (newline)\n"
74 " (call-with-current-continuation escape)\n"
75 " (display x) (newline)\n"
1e6808ea
MG
76 " x)\n"
77 "\n"
b380b885
MD
78 " ;; out-guard:\n"
79 " ;;\n"
1e6808ea
MG
80 " (lambda () (set! x old-x)))))))\n"
81 "\n"
9401323e 82 ";; Prints:\n"
b380b885
MD
83 "special-binding\n"
84 ";; Evaluates to:\n"
1e6808ea 85 "@result{} a-cont\n"
b380b885 86 "x\n"
1e6808ea 87 "@result{} normal-binding\n"
b380b885
MD
88 "(a-cont #f)\n"
89 ";; Prints:\n"
90 "special-binding\n"
91 ";; Evaluates to:\n"
1e6808ea 92 "@result{} a-cont ;; the value of the (define a-cont...)\n"
b380b885 93 "x\n"
1e6808ea 94 "@result{} normal-binding\n"
b380b885
MD
95 "a-cont\n"
96 "@result{} special-binding\n"
1e6808ea 97 "@end lisp")
1bbd0b84 98#define FUNC_NAME s_scm_dynamic_wind
0f2d19dd 99{
9de87eea 100 SCM ans, old_winds;
7888309b 101 SCM_ASSERT (scm_is_true (scm_thunk_p (out_guard)),
1e6808ea 102 out_guard,
1bbd0b84 103 SCM_ARG3, FUNC_NAME);
fdc28395 104 scm_call_0 (in_guard);
9de87eea
MV
105 old_winds = scm_i_dynwinds ();
106 scm_i_set_dynwinds (scm_acons (in_guard, out_guard, old_winds));
fdc28395 107 ans = scm_call_0 (thunk);
9de87eea 108 scm_i_set_dynwinds (old_winds);
fdc28395 109 scm_call_0 (out_guard);
0f2d19dd
JB
110 return ans;
111}
1bbd0b84 112#undef FUNC_NAME
0f2d19dd 113
3346a90f 114SCM
92c2555f
MV
115scm_internal_dynamic_wind (scm_t_guard before,
116 scm_t_inner inner,
117 scm_t_guard after,
3346a90f
MD
118 void *inner_data,
119 void *guard_data)
120{
4845bbae
MV
121 SCM ans;
122
661ae7ab
MV
123 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
124 scm_dynwind_rewind_handler (before, guard_data, SCM_F_WIND_EXPLICITLY);
125 scm_dynwind_unwind_handler (after, guard_data, SCM_F_WIND_EXPLICITLY);
3346a90f 126 ans = inner (inner_data);
661ae7ab 127 scm_dynwind_end ();
3346a90f
MD
128 return ans;
129}
1cc91f1b 130
4845bbae
MV
131/* Frames and winders. */
132
133static scm_t_bits tc16_frame;
134#define FRAME_P(f) SCM_SMOB_PREDICATE (tc16_frame, (f))
135
f5710d53
MV
136#define FRAME_F_REWINDABLE (1 << 0)
137#define FRAME_REWINDABLE_P(f) (SCM_SMOB_FLAGS(f) & FRAME_F_REWINDABLE)
4845bbae
MV
138
139static scm_t_bits tc16_winder;
14578fa4 140#define WINDER_P(w) SCM_SMOB_PREDICATE (tc16_winder, (w))
f5710d53
MV
141#define WINDER_PROC(w) ((void (*)(void *))SCM_SMOB_DATA (w))
142#define WINDER_DATA(w) ((void *)SCM_SMOB_DATA_2 (w))
4845bbae 143
f5710d53
MV
144#define WINDER_F_EXPLICIT (1 << 0)
145#define WINDER_F_REWIND (1 << 1)
146#define WINDER_F_MARK (1 << 2)
147#define WINDER_EXPLICIT_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_EXPLICIT)
148#define WINDER_REWIND_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_REWIND)
149#define WINDER_MARK_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_MARK)
4845bbae
MV
150
151void
661ae7ab 152scm_dynwind_begin (scm_t_dynwind_flags flags)
4845bbae
MV
153{
154 SCM f;
f5710d53 155 SCM_NEWSMOB (f, tc16_frame, 0);
661ae7ab 156 if (flags & SCM_F_DYNWIND_REWINDABLE)
f5710d53 157 SCM_SET_SMOB_FLAGS (f, FRAME_F_REWINDABLE);
9de87eea 158 scm_i_set_dynwinds (scm_cons (f, scm_i_dynwinds ()));
4845bbae
MV
159}
160
161void
661ae7ab 162scm_dynwind_end (void)
4845bbae 163{
9de87eea
MV
164 SCM winds;
165
0888de4f
MV
166 /* Unwind upto and including the next frame entry. We can only
167 encounter #<winder> entries on the way.
4845bbae
MV
168 */
169
9de87eea
MV
170 winds = scm_i_dynwinds ();
171 while (scm_is_pair (winds))
4845bbae 172 {
9de87eea
MV
173 SCM entry = SCM_CAR (winds);
174 winds = SCM_CDR (winds);
175
176 scm_i_set_dynwinds (winds);
0888de4f
MV
177
178 if (FRAME_P (entry))
179 return;
180
181 assert (WINDER_P (entry));
182 if (!WINDER_REWIND_P (entry) && WINDER_EXPLICIT_P (entry))
183 WINDER_PROC(entry) (WINDER_DATA (entry));
4845bbae
MV
184 }
185
186 assert (0);
187}
188
189void
661ae7ab
MV
190scm_dynwind_unwind_handler (void (*proc) (void *), void *data,
191 scm_t_wind_flags flags)
4845bbae
MV
192{
193 SCM w;
f5710d53
MV
194 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, (scm_t_bits) data);
195 if (flags & SCM_F_WIND_EXPLICITLY)
196 SCM_SET_SMOB_FLAGS (w, WINDER_F_EXPLICIT);
9de87eea 197 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
4845bbae
MV
198}
199
200void
661ae7ab
MV
201scm_dynwind_rewind_handler (void (*proc) (void *), void *data,
202 scm_t_wind_flags flags)
4845bbae
MV
203{
204 SCM w;
f5710d53
MV
205 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, (scm_t_bits) data);
206 SCM_SET_SMOB_FLAGS (w, WINDER_F_REWIND);
9de87eea 207 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
a520e4f0
MV
208 if (flags & SCM_F_WIND_EXPLICITLY)
209 proc (data);
210}
211
212void
661ae7ab
MV
213scm_dynwind_unwind_handler_with_scm (void (*proc) (SCM), SCM data,
214 scm_t_wind_flags flags)
a520e4f0
MV
215{
216 SCM w;
217 scm_t_bits fl = ((flags&SCM_F_WIND_EXPLICITLY)? WINDER_F_EXPLICIT : 0);
f5710d53
MV
218 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, SCM_UNPACK (data));
219 SCM_SET_SMOB_FLAGS (w, fl | WINDER_F_MARK);
9de87eea 220 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
a520e4f0
MV
221}
222
223void
661ae7ab
MV
224scm_dynwind_rewind_handler_with_scm (void (*proc) (SCM), SCM data,
225 scm_t_wind_flags flags)
a520e4f0
MV
226{
227 SCM w;
f5710d53
MV
228 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, SCM_UNPACK (data));
229 SCM_SET_SMOB_FLAGS (w, WINDER_F_REWIND | WINDER_F_MARK);
9de87eea 230 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
a520e4f0 231 if (flags & SCM_F_WIND_EXPLICITLY)
4845bbae
MV
232 proc (data);
233}
234
6d5649b7 235void
661ae7ab 236scm_dynwind_free (void *mem)
6d5649b7 237{
661ae7ab 238 scm_dynwind_unwind_handler (free, mem, SCM_F_WIND_EXPLICITLY);
6d5649b7
MV
239}
240
c2654ef0 241#ifdef GUILE_DEBUG
a1ec6916 242SCM_DEFINE (scm_wind_chain, "wind-chain", 0, 0, 0,
1bbd0b84 243 (),
156149ad
MG
244 "Return the current wind chain. The wind chain contains all\n"
245 "information required by @code{dynamic-wind} to call its\n"
246 "argument thunks when entering/exiting its scope.")
1bbd0b84 247#define FUNC_NAME s_scm_wind_chain
c2654ef0 248{
9de87eea 249 return scm_i_dynwinds ();
c2654ef0 250}
1bbd0b84 251#undef FUNC_NAME
c2654ef0
MD
252#endif
253
2e171178 254void
904a077d 255scm_swap_bindings (SCM vars, SCM vals)
6778caf9
MD
256{
257 SCM tmp;
258 while (SCM_NIMP (vals))
259 {
904a077d
MV
260 tmp = SCM_VARIABLE_REF (SCM_CAR (vars));
261 SCM_VARIABLE_SET (SCM_CAR (vars), SCM_CAR (vals));
6778caf9 262 SCM_SETCAR (vals, tmp);
904a077d 263 vars = SCM_CDR (vars);
6778caf9
MD
264 vals = SCM_CDR (vals);
265 }
266}
c2654ef0 267
4845bbae 268void
c014a02e 269scm_dowinds (SCM to, long delta)
4845bbae 270{
14578fa4 271 scm_i_dowinds (to, delta, NULL, NULL);
4845bbae
MV
272}
273
274void
14578fa4 275scm_i_dowinds (SCM to, long delta, void (*turn_func) (void *), void *data)
0f2d19dd
JB
276{
277 tail:
9de87eea 278 if (scm_is_eq (to, scm_i_dynwinds ()))
4845bbae
MV
279 {
280 if (turn_func)
281 turn_func (data);
282 }
1be6b49c 283 else if (delta < 0)
0f2d19dd
JB
284 {
285 SCM wind_elt;
286 SCM wind_key;
287
14578fa4 288 scm_i_dowinds (SCM_CDR (to), 1 + delta, turn_func, data);
0f2d19dd 289 wind_elt = SCM_CAR (to);
4845bbae 290
928e0f42 291 if (FRAME_P (wind_elt))
0f2d19dd 292 {
928e0f42
MV
293 if (!FRAME_REWINDABLE_P (wind_elt))
294 scm_misc_error ("dowinds",
295 "cannot invoke continuation from this context",
296 SCM_EOL);
297 }
298 else if (WINDER_P (wind_elt))
299 {
300 if (WINDER_REWIND_P (wind_elt))
301 WINDER_PROC (wind_elt) (WINDER_DATA (wind_elt));
0f2d19dd
JB
302 }
303 else
0f2d19dd 304 {
928e0f42
MV
305 wind_key = SCM_CAR (wind_elt);
306 /* key = #t | symbol | thunk | list of variables */
307 if (SCM_NIMP (wind_key))
4845bbae 308 {
d2e53ed6 309 if (scm_is_pair (wind_key))
4845bbae 310 {
928e0f42
MV
311 if (SCM_VARIABLEP (SCM_CAR (wind_key)))
312 scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
904a077d 313 }
928e0f42
MV
314 else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
315 scm_call_0 (wind_key);
b3460a50 316 }
0f2d19dd 317 }
928e0f42 318
9de87eea 319 scm_i_set_dynwinds (to);
0f2d19dd
JB
320 }
321 else
322 {
9de87eea 323 SCM wind;
0f2d19dd
JB
324 SCM wind_elt;
325 SCM wind_key;
326
9de87eea
MV
327 wind = scm_i_dynwinds ();
328 wind_elt = SCM_CAR (wind);
329 scm_i_set_dynwinds (SCM_CDR (wind));
4845bbae 330
928e0f42 331 if (FRAME_P (wind_elt))
0f2d19dd 332 {
928e0f42
MV
333 /* Nothing to do. */
334 }
335 else if (WINDER_P (wind_elt))
336 {
337 if (!WINDER_REWIND_P (wind_elt))
338 WINDER_PROC (wind_elt) (WINDER_DATA (wind_elt));
0f2d19dd
JB
339 }
340 else
0f2d19dd 341 {
928e0f42
MV
342 wind_key = SCM_CAR (wind_elt);
343 if (SCM_NIMP (wind_key))
4845bbae 344 {
d2e53ed6 345 if (scm_is_pair (wind_key))
4845bbae 346 {
928e0f42
MV
347 if (SCM_VARIABLEP (SCM_CAR (wind_key)))
348 scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
904a077d 349 }
928e0f42
MV
350 else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
351 scm_call_0 (SCM_CDR (wind_elt));
b3460a50 352 }
0f2d19dd 353 }
928e0f42 354
0f2d19dd
JB
355 delta--;
356 goto tail; /* scm_dowinds(to, delta-1); */
357 }
358}
359
0f2d19dd
JB
360void
361scm_init_dynwind ()
0f2d19dd 362{
4845bbae 363 tc16_frame = scm_make_smob_type ("frame", 0);
4845bbae
MV
364
365 tc16_winder = scm_make_smob_type ("winder", 0);
366
a0599745 367#include "libguile/dynwind.x"
0f2d19dd 368}
89e00824
ML
369
370/*
371 Local Variables:
372 c-file-style: "gnu"
373 End:
374*/