dynwind.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 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 <assert.h>
  21. #include "libguile/_scm.h"
  22. #include "libguile/eval.h"
  23. #include "libguile/alist.h"
  24. #include "libguile/fluids.h"
  25. #include "libguile/ports.h"
  26. #include "libguile/smob.h"
  27. #include "libguile/dynwind.h"
  28. /* {Dynamic wind}
  29. Things that can be on the wind list:
  30. #<frame>
  31. #<winder>
  32. (enter-proc . leave-proc) dynamic-wind
  33. (tag . jmpbuf) catch
  34. (tag . pre-unwind-data) throw-handler / lazy-catch
  35. tag is either a symbol or a boolean
  36. */
  37. SCM_DEFINE (scm_dynamic_wind, "dynamic-wind", 3, 0, 0,
  38. (SCM in_guard, SCM thunk, SCM out_guard),
  39. "All three arguments must be 0-argument procedures.\n"
  40. "@var{in_guard} is called, then @var{thunk}, then\n"
  41. "@var{out_guard}.\n"
  42. "\n"
  43. "If, any time during the execution of @var{thunk}, the\n"
  44. "continuation of the @code{dynamic_wind} expression is escaped\n"
  45. "non-locally, @var{out_guard} is called. If the continuation of\n"
  46. "the dynamic-wind is re-entered, @var{in_guard} is called. Thus\n"
  47. "@var{in_guard} and @var{out_guard} may be called any number of\n"
  48. "times.\n"
  49. "@lisp\n"
  50. "(define x 'normal-binding)\n"
  51. "@result{} x\n"
  52. "(define a-cont (call-with-current-continuation\n"
  53. " (lambda (escape)\n"
  54. " (let ((old-x x))\n"
  55. " (dynamic-wind\n"
  56. " ;; in-guard:\n"
  57. " ;;\n"
  58. " (lambda () (set! x 'special-binding))\n"
  59. "\n"
  60. " ;; thunk\n"
  61. " ;;\n"
  62. " (lambda () (display x) (newline)\n"
  63. " (call-with-current-continuation escape)\n"
  64. " (display x) (newline)\n"
  65. " x)\n"
  66. "\n"
  67. " ;; out-guard:\n"
  68. " ;;\n"
  69. " (lambda () (set! x old-x)))))))\n"
  70. "\n"
  71. ";; Prints:\n"
  72. "special-binding\n"
  73. ";; Evaluates to:\n"
  74. "@result{} a-cont\n"
  75. "x\n"
  76. "@result{} normal-binding\n"
  77. "(a-cont #f)\n"
  78. ";; Prints:\n"
  79. "special-binding\n"
  80. ";; Evaluates to:\n"
  81. "@result{} a-cont ;; the value of the (define a-cont...)\n"
  82. "x\n"
  83. "@result{} normal-binding\n"
  84. "a-cont\n"
  85. "@result{} special-binding\n"
  86. "@end lisp")
  87. #define FUNC_NAME s_scm_dynamic_wind
  88. {
  89. SCM ans, old_winds;
  90. SCM_ASSERT (scm_is_true (scm_thunk_p (out_guard)),
  91. out_guard,
  92. SCM_ARG3, FUNC_NAME);
  93. scm_call_0 (in_guard);
  94. old_winds = scm_i_dynwinds ();
  95. scm_i_set_dynwinds (scm_acons (in_guard, out_guard, old_winds));
  96. ans = scm_call_0 (thunk);
  97. scm_i_set_dynwinds (old_winds);
  98. scm_call_0 (out_guard);
  99. return ans;
  100. }
  101. #undef FUNC_NAME
  102. SCM
  103. scm_internal_dynamic_wind (scm_t_guard before,
  104. scm_t_inner inner,
  105. scm_t_guard after,
  106. void *inner_data,
  107. void *guard_data)
  108. {
  109. SCM ans;
  110. scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
  111. scm_dynwind_rewind_handler (before, guard_data, SCM_F_WIND_EXPLICITLY);
  112. scm_dynwind_unwind_handler (after, guard_data, SCM_F_WIND_EXPLICITLY);
  113. ans = inner (inner_data);
  114. scm_dynwind_end ();
  115. return ans;
  116. }
  117. /* Frames and winders. */
  118. static scm_t_bits tc16_frame;
  119. #define FRAME_P(f) SCM_SMOB_PREDICATE (tc16_frame, (f))
  120. #define FRAME_F_REWINDABLE (1 << 0)
  121. #define FRAME_REWINDABLE_P(f) (SCM_SMOB_FLAGS(f) & FRAME_F_REWINDABLE)
  122. static scm_t_bits tc16_winder;
  123. #define WINDER_P(w) SCM_SMOB_PREDICATE (tc16_winder, (w))
  124. #define WINDER_PROC(w) ((void (*)(void *))SCM_SMOB_DATA (w))
  125. #define WINDER_DATA(w) ((void *)SCM_SMOB_DATA_2 (w))
  126. #define WINDER_F_EXPLICIT (1 << 0)
  127. #define WINDER_F_REWIND (1 << 1)
  128. #define WINDER_F_MARK (1 << 2)
  129. #define WINDER_EXPLICIT_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_EXPLICIT)
  130. #define WINDER_REWIND_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_REWIND)
  131. #define WINDER_MARK_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_MARK)
  132. void
  133. scm_dynwind_begin (scm_t_dynwind_flags flags)
  134. {
  135. SCM f;
  136. SCM_NEWSMOB (f, tc16_frame, 0);
  137. if (flags & SCM_F_DYNWIND_REWINDABLE)
  138. SCM_SET_SMOB_FLAGS (f, FRAME_F_REWINDABLE);
  139. scm_i_set_dynwinds (scm_cons (f, scm_i_dynwinds ()));
  140. }
  141. void
  142. scm_dynwind_end (void)
  143. {
  144. SCM winds;
  145. /* Unwind upto and including the next frame entry. We can only
  146. encounter #<winder> entries on the way.
  147. */
  148. winds = scm_i_dynwinds ();
  149. while (scm_is_pair (winds))
  150. {
  151. SCM entry = SCM_CAR (winds);
  152. winds = SCM_CDR (winds);
  153. scm_i_set_dynwinds (winds);
  154. if (FRAME_P (entry))
  155. return;
  156. assert (WINDER_P (entry));
  157. if (!WINDER_REWIND_P (entry) && WINDER_EXPLICIT_P (entry))
  158. WINDER_PROC(entry) (WINDER_DATA (entry));
  159. }
  160. assert (0);
  161. }
  162. static SCM
  163. winder_mark (SCM w)
  164. {
  165. if (WINDER_MARK_P (w))
  166. return SCM_PACK (WINDER_DATA (w));
  167. return SCM_BOOL_F;
  168. }
  169. void
  170. scm_dynwind_unwind_handler (void (*proc) (void *), void *data,
  171. scm_t_wind_flags flags)
  172. {
  173. SCM w;
  174. SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, (scm_t_bits) data);
  175. if (flags & SCM_F_WIND_EXPLICITLY)
  176. SCM_SET_SMOB_FLAGS (w, WINDER_F_EXPLICIT);
  177. scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
  178. }
  179. void
  180. scm_dynwind_rewind_handler (void (*proc) (void *), void *data,
  181. scm_t_wind_flags flags)
  182. {
  183. SCM w;
  184. SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, (scm_t_bits) data);
  185. SCM_SET_SMOB_FLAGS (w, WINDER_F_REWIND);
  186. scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
  187. if (flags & SCM_F_WIND_EXPLICITLY)
  188. proc (data);
  189. }
  190. void
  191. scm_dynwind_unwind_handler_with_scm (void (*proc) (SCM), SCM data,
  192. scm_t_wind_flags flags)
  193. {
  194. SCM w;
  195. scm_t_bits fl = ((flags&SCM_F_WIND_EXPLICITLY)? WINDER_F_EXPLICIT : 0);
  196. SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, SCM_UNPACK (data));
  197. SCM_SET_SMOB_FLAGS (w, fl | WINDER_F_MARK);
  198. scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
  199. }
  200. void
  201. scm_dynwind_rewind_handler_with_scm (void (*proc) (SCM), SCM data,
  202. scm_t_wind_flags flags)
  203. {
  204. SCM w;
  205. SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, SCM_UNPACK (data));
  206. SCM_SET_SMOB_FLAGS (w, WINDER_F_REWIND | WINDER_F_MARK);
  207. scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
  208. if (flags & SCM_F_WIND_EXPLICITLY)
  209. proc (data);
  210. }
  211. void
  212. scm_dynwind_free (void *mem)
  213. {
  214. scm_dynwind_unwind_handler (free, mem, SCM_F_WIND_EXPLICITLY);
  215. }
  216. #ifdef GUILE_DEBUG
  217. SCM_DEFINE (scm_wind_chain, "wind-chain", 0, 0, 0,
  218. (),
  219. "Return the current wind chain. The wind chain contains all\n"
  220. "information required by @code{dynamic-wind} to call its\n"
  221. "argument thunks when entering/exiting its scope.")
  222. #define FUNC_NAME s_scm_wind_chain
  223. {
  224. return scm_i_dynwinds ();
  225. }
  226. #undef FUNC_NAME
  227. #endif
  228. void
  229. scm_swap_bindings (SCM vars, SCM vals)
  230. {
  231. SCM tmp;
  232. while (SCM_NIMP (vals))
  233. {
  234. tmp = SCM_VARIABLE_REF (SCM_CAR (vars));
  235. SCM_VARIABLE_SET (SCM_CAR (vars), SCM_CAR (vals));
  236. SCM_SETCAR (vals, tmp);
  237. vars = SCM_CDR (vars);
  238. vals = SCM_CDR (vals);
  239. }
  240. }
  241. void
  242. scm_dowinds (SCM to, long delta)
  243. {
  244. scm_i_dowinds (to, delta, NULL, NULL);
  245. }
  246. void
  247. scm_i_dowinds (SCM to, long delta, void (*turn_func) (void *), void *data)
  248. {
  249. tail:
  250. if (scm_is_eq (to, scm_i_dynwinds ()))
  251. {
  252. if (turn_func)
  253. turn_func (data);
  254. }
  255. else if (delta < 0)
  256. {
  257. SCM wind_elt;
  258. SCM wind_key;
  259. scm_i_dowinds (SCM_CDR (to), 1 + delta, turn_func, data);
  260. wind_elt = SCM_CAR (to);
  261. if (FRAME_P (wind_elt))
  262. {
  263. if (!FRAME_REWINDABLE_P (wind_elt))
  264. scm_misc_error ("dowinds",
  265. "cannot invoke continuation from this context",
  266. SCM_EOL);
  267. }
  268. else if (WINDER_P (wind_elt))
  269. {
  270. if (WINDER_REWIND_P (wind_elt))
  271. WINDER_PROC (wind_elt) (WINDER_DATA (wind_elt));
  272. }
  273. else
  274. {
  275. wind_key = SCM_CAR (wind_elt);
  276. /* key = #t | symbol | thunk | list of variables */
  277. if (SCM_NIMP (wind_key))
  278. {
  279. if (scm_is_pair (wind_key))
  280. {
  281. if (SCM_VARIABLEP (SCM_CAR (wind_key)))
  282. scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
  283. }
  284. else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
  285. scm_call_0 (wind_key);
  286. }
  287. }
  288. scm_i_set_dynwinds (to);
  289. }
  290. else
  291. {
  292. SCM wind;
  293. SCM wind_elt;
  294. SCM wind_key;
  295. wind = scm_i_dynwinds ();
  296. wind_elt = SCM_CAR (wind);
  297. scm_i_set_dynwinds (SCM_CDR (wind));
  298. if (FRAME_P (wind_elt))
  299. {
  300. /* Nothing to do. */
  301. }
  302. else if (WINDER_P (wind_elt))
  303. {
  304. if (!WINDER_REWIND_P (wind_elt))
  305. WINDER_PROC (wind_elt) (WINDER_DATA (wind_elt));
  306. }
  307. else
  308. {
  309. wind_key = SCM_CAR (wind_elt);
  310. if (SCM_NIMP (wind_key))
  311. {
  312. if (scm_is_pair (wind_key))
  313. {
  314. if (SCM_VARIABLEP (SCM_CAR (wind_key)))
  315. scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
  316. }
  317. else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
  318. scm_call_0 (SCM_CDR (wind_elt));
  319. }
  320. }
  321. delta--;
  322. goto tail; /* scm_dowinds(to, delta-1); */
  323. }
  324. }
  325. void
  326. scm_init_dynwind ()
  327. {
  328. tc16_frame = scm_make_smob_type ("frame", 0);
  329. tc16_winder = scm_make_smob_type ("winder", 0);
  330. scm_set_smob_mark (tc16_winder, winder_mark);
  331. #include "libguile/dynwind.x"
  332. }
  333. /*
  334. Local Variables:
  335. c-file-style: "gnu"
  336. End:
  337. */