continuations.c 12 KB

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