Remove trailing whitespace
[bpt/guile.git] / libguile / continuations.c
CommitLineData
dbb605f5 1/* Copyright (C) 1995,1996,1998,2000,2001,2004, 2006, 2008 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
0f2d19dd 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
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
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
73be1d9e 17 */
1bbd0b84 18
1bbd0b84 19
0f2d19dd 20\f
dbb605f5
LC
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
0f2d19dd 24
bbd43f03
RB
25#include "libguile/_scm.h"
26
13070bd3 27#include <string.h>
8b7f0bb3 28#include <stdio.h>
13070bd3 29
8b7f0bb3 30#include "libguile/async.h"
d0f6ceb8 31#include "libguile/debug.h"
a0599745
MD
32#include "libguile/root.h"
33#include "libguile/stackchk.h"
5f144b10
GH
34#include "libguile/smob.h"
35#include "libguile/ports.h"
36#include "libguile/dynwind.h"
ce212434 37#include "libguile/values.h"
9de87eea 38#include "libguile/eval.h"
bfffd258 39#include "libguile/vm.h"
5f144b10 40
db4b4ca6 41#include "libguile/validate.h"
a0599745 42#include "libguile/continuations.h"
01c8a3dd 43
0f2d19dd
JB
44\f
45
46/* {Continuations}
47 */
48
92c2555f 49scm_t_bits scm_tc16_continuation;
0f2d19dd 50
e841c3e0
KN
51static SCM
52continuation_mark (SCM obj)
5f144b10 53{
92c2555f 54 scm_t_contregs *continuation = SCM_CONTREGS (obj);
01c8a3dd 55
9de87eea 56 scm_gc_mark (continuation->root);
5f144b10 57 scm_gc_mark (continuation->throw_value);
7ff01700 58 scm_gc_mark (continuation->vm_conts);
5f144b10 59 scm_mark_locations (continuation->stack, continuation->num_stack_items);
193297d8
RB
60#ifdef __ia64__
61 if (continuation->backing_store)
62 scm_mark_locations (continuation->backing_store,
63 continuation->backing_store_size /
64 sizeof (SCM_STACKITEM));
65#endif /* __ia64__ */
5f144b10
GH
66 return continuation->dynenv;
67}
01c8a3dd 68
1be6b49c 69static size_t
e841c3e0 70continuation_free (SCM obj)
5f144b10 71{
92c2555f 72 scm_t_contregs *continuation = SCM_CONTREGS (obj);
9de87eea 73 /* stack array size is 1 if num_stack_items is 0. */
1be6b49c 74 size_t extra_items = (continuation->num_stack_items > 0)
5f144b10
GH
75 ? (continuation->num_stack_items - 1)
76 : 0;
92c2555f 77 size_t bytes_free = sizeof (scm_t_contregs)
5f144b10 78 + extra_items * sizeof (SCM_STACKITEM);
193297d8
RB
79
80#ifdef __ia64__
4c9419ac
MV
81 scm_gc_free (continuation->backing_store, continuation->backing_store_size,
82 "continuation backing store");
193297d8 83#endif /* __ia64__ */
4c9419ac
MV
84 scm_gc_free (continuation, bytes_free, "continuation");
85 return 0;
5f144b10 86}
01c8a3dd 87
e841c3e0 88static int
e81d98ec 89continuation_print (SCM obj, SCM port, scm_print_state *state SCM_UNUSED)
5f144b10 90{
92c2555f 91 scm_t_contregs *continuation = SCM_CONTREGS (obj);
5f144b10
GH
92
93 scm_puts ("#<continuation ", port);
94 scm_intprint (continuation->num_stack_items, 10, port);
95 scm_puts (" @ ", port);
0345e278 96 scm_uintprint (SCM_CELL_WORD_1 (obj), 16, port);
5f144b10
GH
97 scm_putc ('>', port);
98 return 1;
99}
1cc91f1b 100
5f144b10
GH
101/* this may return more than once: the first time with the escape
102 procedure, then subsequently with the value to be passed to the
103 continuation. */
104#define FUNC_NAME "scm_make_continuation"
0f2d19dd 105SCM
5f144b10 106scm_make_continuation (int *first)
0f2d19dd 107{
9de87eea
MV
108 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
109 SCM cont;
92c2555f 110 scm_t_contregs *continuation;
c014a02e 111 long stack_size;
01c8a3dd 112 SCM_STACKITEM * src;
0f2d19dd 113
0f2d19dd 114 SCM_FLUSH_REGISTER_WINDOWS;
9de87eea 115 stack_size = scm_stack_size (thread->continuation_base);
4c9419ac
MV
116 continuation = scm_gc_malloc (sizeof (scm_t_contregs)
117 + (stack_size - 1) * sizeof (SCM_STACKITEM),
118 "continuation");
5f144b10 119 continuation->num_stack_items = stack_size;
9de87eea 120 continuation->dynenv = scm_i_dynwinds ();
5f144b10 121 continuation->throw_value = SCM_EOL;
9de87eea
MV
122 continuation->root = thread->continuation_root;
123 continuation->dframe = scm_i_last_debug_frame ();
124 src = thread->continuation_base;
4ccb2cd2 125#if ! SCM_STACK_GROWS_UP
5f144b10
GH
126 src -= stack_size;
127#endif
5c5c27dc 128 continuation->offset = continuation->stack - src;
5f144b10 129 memcpy (continuation->stack, src, sizeof (SCM_STACKITEM) * stack_size);
bfffd258 130 continuation->vm_conts = scm_vm_capture_continuations ();
5f144b10 131
79824460
AW
132 SCM_NEWSMOB (cont, scm_tc16_continuation, continuation);
133
346e4402
NJ
134 *first = !setjmp (continuation->jmpbuf);
135 if (*first)
193297d8 136 {
346e4402 137#ifdef __ia64__
9a5fa6e9 138 continuation->backing_store_size =
346e4402 139 (char *) scm_ia64_ar_bsp(&continuation->jmpbuf.ctx)
9a5fa6e9 140 -
346e4402 141 (char *) thread->register_backing_store_base;
193297d8
RB
142 continuation->backing_store = NULL;
143 continuation->backing_store =
4c9419ac
MV
144 scm_gc_malloc (continuation->backing_store_size,
145 "continuation backing store");
193297d8 146 memcpy (continuation->backing_store,
346e4402 147 (void *) thread->register_backing_store_base,
193297d8 148 continuation->backing_store_size);
346e4402 149#endif /* __ia64__ */
193297d8
RB
150 return cont;
151 }
152 else
153 {
3c468478 154 SCM ret = continuation->throw_value;
3c468478
MV
155 continuation->throw_value = SCM_BOOL_F;
156 return ret;
193297d8 157 }
0f2d19dd 158}
5f144b10 159#undef FUNC_NAME
0f2d19dd 160
d3c6aef9
MV
161
162/* Invoking a continuation proceeds as follows:
163 *
164 * - the stack is made large enough for the called continuation
165 * - the old windchain is unwound down to the branching point
166 * - the continuation stack is copied into place
167 * - the windchain is rewound up to the continuation's context
168 * - the continuation is invoked via longjmp (or setcontext)
169 *
170 * This order is important so that unwind and rewind handlers are run
171 * with their correct stack.
172 */
173
5f144b10 174static void scm_dynthrow (SCM, SCM);
01c8a3dd
DH
175
176/* Grow the stack by a fixed amount to provide space to copy in the
177 * continuation. Possibly this function has to be called several times
178 * recursively before enough space is available. Make sure the compiler does
179 * not optimize the growth array away by storing it's address into a global
180 * variable.
181 */
182
92c2555f 183scm_t_bits scm_i_dummy;
1cc91f1b 184
0f2d19dd 185static void
01c8a3dd
DH
186grow_stack (SCM cont, SCM val)
187{
92c2555f 188 scm_t_bits growth[100];
01c8a3dd 189
92c2555f 190 scm_i_dummy = (scm_t_bits) growth;
01c8a3dd 191 scm_dynthrow (cont, val);
0f2d19dd 192}
0f2d19dd 193
1cc91f1b 194
01c8a3dd
DH
195/* Copy the continuation stack into the current stack. Calling functions from
196 * within this function is safe, since only stack frames below this function's
197 * own frame are overwritten. Thus, memcpy can be used for best performance.
198 */
d3c6aef9
MV
199
200typedef struct {
201 scm_t_contregs *continuation;
202 SCM_STACKITEM *dst;
203} copy_stack_data;
204
01c8a3dd 205static void
d3c6aef9
MV
206copy_stack (void *data)
207{
208 copy_stack_data *d = (copy_stack_data *)data;
209 memcpy (d->dst, d->continuation->stack,
210 sizeof (SCM_STACKITEM) * d->continuation->num_stack_items);
bfffd258 211 scm_vm_reinstate_continuations (d->continuation->vm_conts);
346e4402
NJ
212#ifdef __ia64__
213 SCM_I_CURRENT_THREAD->pending_rbs_continuation = d->continuation;
214#endif
d3c6aef9
MV
215}
216
217static void
218copy_stack_and_call (scm_t_contregs *continuation, SCM val,
5f144b10 219 SCM_STACKITEM * dst)
0f2d19dd 220{
d3c6aef9
MV
221 long delta;
222 copy_stack_data data;
223
9de87eea 224 delta = scm_ilength (scm_i_dynwinds ()) - scm_ilength (continuation->dynenv);
d3c6aef9
MV
225 data.continuation = continuation;
226 data.dst = dst;
14578fa4 227 scm_i_dowinds (continuation->dynenv, delta, copy_stack, &data);
01c8a3dd 228
9de87eea 229 scm_i_set_last_debug_frame (continuation->dframe);
01c8a3dd 230
5f144b10
GH
231 continuation->throw_value = val;
232 longjmp (continuation->jmpbuf, 1);
01c8a3dd
DH
233}
234
346e4402
NJ
235#ifdef __ia64__
236void
237scm_ia64_longjmp (jmp_buf *JB, int VAL)
238{
239 scm_i_thread *t = SCM_I_CURRENT_THREAD;
240
241 if (t->pending_rbs_continuation)
242 {
243 memcpy (t->register_backing_store_base,
244 t->pending_rbs_continuation->backing_store,
245 t->pending_rbs_continuation->backing_store_size);
246 t->pending_rbs_continuation = NULL;
247 }
248 setcontext (&JB->ctx);
249}
250#endif
251
01c8a3dd
DH
252/* Call grow_stack until the stack space is large enough, then, as the current
253 * stack frame might get overwritten, let copy_stack_and_call perform the
254 * actual copying and continuation calling.
255 */
256static void
257scm_dynthrow (SCM cont, SCM val)
258{
9de87eea 259 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
92c2555f 260 scm_t_contregs *continuation = SCM_CONTREGS (cont);
9de87eea 261 SCM_STACKITEM *dst = thread->continuation_base;
01c8a3dd
DH
262 SCM_STACKITEM stack_top_element;
263
8b7f0bb3
MV
264 if (scm_i_critical_section_level)
265 {
266 fprintf (stderr, "continuation invoked from within critical section.\n");
267 abort ();
268 }
269
4ccb2cd2 270#if SCM_STACK_GROWS_UP
5afcf08b 271 if (dst + continuation->num_stack_items >= &stack_top_element)
01c8a3dd 272 grow_stack (cont, val);
0f2d19dd 273#else
5f144b10 274 dst -= continuation->num_stack_items;
c8a1bdc4 275 if (dst <= &stack_top_element)
01c8a3dd 276 grow_stack (cont, val);
0f2d19dd 277#endif /* def SCM_STACK_GROWS_UP */
01c8a3dd 278
5f144b10
GH
279 SCM_FLUSH_REGISTER_WINDOWS;
280 copy_stack_and_call (continuation, val, dst);
0f2d19dd
JB
281}
282
db4b4ca6
DH
283
284static SCM
285continuation_apply (SCM cont, SCM args)
5f144b10 286#define FUNC_NAME "continuation_apply"
0f2d19dd 287{
9de87eea 288 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
92c2555f 289 scm_t_contregs *continuation = SCM_CONTREGS (cont);
5f144b10 290
9de87eea 291 if (continuation->root != thread->continuation_root)
5f144b10 292 {
9de87eea
MV
293 SCM_MISC_ERROR
294 ("invoking continuation would cross continuation barrier: ~A",
295 scm_list_1 (cont));
5f144b10 296 }
0f2d19dd 297
ce212434 298 scm_dynthrow (cont, scm_values (args));
0f2d19dd
JB
299 return SCM_UNSPECIFIED; /* not reached */
300}
5f144b10 301#undef FUNC_NAME
0f2d19dd 302
9de87eea
MV
303SCM
304scm_i_with_continuation_barrier (scm_t_catch_body body,
305 void *body_data,
306 scm_t_catch_handler handler,
43e01b1e
NJ
307 void *handler_data,
308 scm_t_catch_handler pre_unwind_handler,
309 void *pre_unwind_handler_data)
9de87eea
MV
310{
311 SCM_STACKITEM stack_item;
312 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
313 SCM old_controot;
314 SCM_STACKITEM *old_contbase;
315 scm_t_debug_frame *old_lastframe;
316 SCM result;
317
318 /* Establish a fresh continuation root.
319 */
320 old_controot = thread->continuation_root;
321 old_contbase = thread->continuation_base;
322 old_lastframe = thread->last_debug_frame;
323 thread->continuation_root = scm_cons (thread->handle, old_controot);
324 thread->continuation_base = &stack_item;
325 thread->last_debug_frame = NULL;
326
327 /* Call FUNC inside a catch all. This is now guaranteed to return
328 directly and exactly once.
329 */
43e01b1e
NJ
330 result = scm_c_catch (SCM_BOOL_T,
331 body, body_data,
332 handler, handler_data,
333 pre_unwind_handler, pre_unwind_handler_data);
9de87eea
MV
334
335 /* Return to old continuation root.
336 */
337 thread->last_debug_frame = old_lastframe;
338 thread->continuation_base = old_contbase;
339 thread->continuation_root = old_controot;
340
341 return result;
342}
343
344struct c_data {
345 void *(*func) (void *);
346 void *data;
347 void *result;
348};
349
350static SCM
351c_body (void *d)
352{
353 struct c_data *data = (struct c_data *)d;
354 data->result = data->func (data->data);
355 return SCM_UNSPECIFIED;
356}
357
358static SCM
359c_handler (void *d, SCM tag, SCM args)
360{
361 struct c_data *data = (struct c_data *)d;
9de87eea
MV
362 data->result = NULL;
363 return SCM_UNSPECIFIED;
364}
365
366void *
367scm_c_with_continuation_barrier (void *(*func) (void *), void *data)
368{
369 struct c_data c_data;
370 c_data.func = func;
371 c_data.data = data;
372 scm_i_with_continuation_barrier (c_body, &c_data,
43e01b1e
NJ
373 c_handler, &c_data,
374 scm_handle_by_message_noexit, NULL);
9de87eea
MV
375 return c_data.result;
376}
377
378struct scm_data {
379 SCM proc;
380};
381
382static SCM
383scm_body (void *d)
384{
385 struct scm_data *data = (struct scm_data *)d;
386 return scm_call_0 (data->proc);
387}
388
389static SCM
390scm_handler (void *d, SCM tag, SCM args)
391{
9de87eea
MV
392 return SCM_BOOL_F;
393}
394
395SCM_DEFINE (scm_with_continuation_barrier, "with-continuation-barrier", 1,0,0,
396 (SCM proc),
69d2000d
MV
397"Call @var{proc} and return its result. Do not allow the invocation of\n"
398"continuations that would leave or enter the dynamic extent of the call\n"
399"to @code{with-continuation-barrier}. Such an attempt causes an error\n"
400"to be signaled.\n"
401"\n"
402"Throws (such as errors) that are not caught from within @var{proc} are\n"
403"caught by @code{with-continuation-barrier}. In that case, a short\n"
404"message is printed to the current error port and @code{#f} is returned.\n"
405"\n"
406"Thus, @code{with-continuation-barrier} returns exactly once.\n")
9de87eea
MV
407#define FUNC_NAME s_scm_with_continuation_barrier
408{
409 struct scm_data scm_data;
410 scm_data.proc = proc;
411 return scm_i_with_continuation_barrier (scm_body, &scm_data,
43e01b1e
NJ
412 scm_handler, &scm_data,
413 scm_handle_by_message_noexit, NULL);
9de87eea
MV
414}
415#undef FUNC_NAME
db4b4ca6 416
0f2d19dd
JB
417void
418scm_init_continuations ()
0f2d19dd 419{
5f144b10
GH
420 scm_tc16_continuation = scm_make_smob_type ("continuation", 0);
421 scm_set_smob_mark (scm_tc16_continuation, continuation_mark);
422 scm_set_smob_free (scm_tc16_continuation, continuation_free);
423 scm_set_smob_print (scm_tc16_continuation, continuation_print);
424 scm_set_smob_apply (scm_tc16_continuation, continuation_apply, 0, 0, 1);
a0599745 425#include "libguile/continuations.x"
0f2d19dd
JB
426}
427
89e00824
ML
428/*
429 Local Variables:
430 c-file-style: "gnu"
431 End:
432*/