For Scheme code, the fundamental procedure to react to non-local entry
and exits of dynamic contexts is dynamic-wind
. C code could
use scm_internal_dynamic_wind
, but since C does not allow the
convenient construction of anonymous procedures that close over
lexical variables, this will be, well, inconvenient.
Therefore, Guile offers the functions scm_dynwind_begin
and
scm_dynwind_end
to delimit a dynamic extent. Within this
dynamic extent, which is called a dynwind context, you can
perform various dynwind actions that control what happens when
the dynwind context is entered or left. For example, you can register
a cleanup routine with scm_dynwind_unwind_handler
that is
executed when the context is left. There are several other more
specialized dynwind actions as well, for example to temporarily block
the execution of asyncs or to temporarily change the current output
port. They are described elsewhere in this manual.
Here is an example that shows how to prevent memory leaks.
/* Suppose there is a function called FOO in some library that you would like to make available to Scheme code (or to C code that follows the Scheme conventions). FOO takes two C strings and returns a new string. When an error has occurred in FOO, it returns NULL. */ char *foo (char *s1, char *s2); /* SCM_FOO interfaces the C function FOO to the Scheme way of life. It takes care to free up all temporary strings in the case of non-local exits. */ SCM scm_foo (SCM s1, SCM s2) { char *c_s1, *c_s2, *c_res; scm_dynwind_begin (0); c_s1 = scm_to_locale_string (s1); /* Call 'free (c_s1)' when the dynwind context is left. */ scm_dynwind_unwind_handler (free, c_s1, SCM_F_WIND_EXPLICITLY); c_s2 = scm_to_locale_string (s2); /* Same as above, but more concisely. */ scm_dynwind_free (c_s2); c_res = foo (c_s1, c_s2); if (c_res == NULL) scm_report_out_of_memory (); scm_dynwind_end (); return scm_take_locale_string (res); }
All three arguments must be 0-argument procedures. in_guard is called, then thunk, then out_guard.
If, any time during the execution of thunk, the
dynamic extent of the dynamic-wind
expression is escaped
non-locally, out_guard is called. If the dynamic extent of
the dynamic-wind is re-entered, in_guard is called. Thus
in_guard and out_guard may be called any number of
times.
(define x 'normal-binding) ⇒ x (define a-cont (call-with-current-continuation (lambda (escape) (let ((old-x x)) (dynamic-wind ;; in-guard: ;; (lambda () (set! x 'special-binding)) ;; thunk ;; (lambda () (display x) (newline) (call-with-current-continuation escape) (display x) (newline) x) ;; out-guard: ;; (lambda () (set! x old-x))))))) ;; Prints: special-binding ;; Evaluates to: ⇒ a-cont x ⇒ normal-binding (a-cont #f) ;; Prints: special-binding ;; Evaluates to: ⇒ a-cont ;; the value of the (define a-cont...) x ⇒ normal-binding a-cont ⇒ special-binding
This is an enumeration of several flags that modify the behavior of
scm_dynwind_begin
. The flags are listed in the following
table.
SCM_F_DYNWIND_REWINDABLE
The dynamic context is rewindable. This means that it can be reentered non-locally (via the invocation of a continuation). The default is that a dynwind context can not be reentered non-locally.
void
scm_dynwind_begin (scm_t_dynwind_flags flags)
¶The function scm_dynwind_begin
starts a new dynamic context and
makes it the ‘current’ one.
The flags argument determines the default behavior of the
context. Normally, use 0. This will result in a context that can not
be reentered with a captured continuation. When you are prepared to
handle reentries, include SCM_F_DYNWIND_REWINDABLE
in
flags.
Being prepared for reentry means that the effects of unwind handlers can be undone on reentry. In the example above, we want to prevent a memory leak on non-local exit and thus register an unwind handler that frees the memory. But once the memory is freed, we can not get it back on reentry. Thus reentry can not be allowed.
The consequence is that continuations become less useful when non-reentrant contexts are captured, but you don’t need to worry about that too much.
The context is ended either implicitly when a non-local exit happens,
or explicitly with scm_dynwind_end
. You must make sure that a
dynwind context is indeed ended properly. If you fail to call
scm_dynwind_end
for each scm_dynwind_begin
, the behavior
is undefined.
void
scm_dynwind_end ()
¶End the current dynamic context explicitly and make the previous one current.
This is an enumeration of several flags that modify the behavior of
scm_dynwind_unwind_handler
and
scm_dynwind_rewind_handler
. The flags are listed in the
following table.
SCM_F_WIND_EXPLICITLY
¶The registered action is also carried out when the dynwind context is entered or left locally.
void
scm_dynwind_unwind_handler (void (*func)(void *), void *data, scm_t_wind_flags flags)
¶void
scm_dynwind_unwind_handler_with_scm (void (*func)(SCM), SCM data, scm_t_wind_flags flags)
¶Arranges for func to be called with data as its arguments
when the current context ends implicitly. If flags contains
SCM_F_WIND_EXPLICITLY
, func is also called when the
context ends explicitly with scm_dynwind_end
.
The function scm_dynwind_unwind_handler_with_scm
takes care that
data is protected from garbage collection.
void
scm_dynwind_rewind_handler (void (*func)(void *), void *data, scm_t_wind_flags flags)
¶void
scm_dynwind_rewind_handler_with_scm (void (*func)(SCM), SCM data, scm_t_wind_flags flags)
¶Arrange for func to be called with data as its argument when
the current context is restarted by rewinding the stack. When flags
contains SCM_F_WIND_EXPLICITLY
, func is called immediately
as well.
The function scm_dynwind_rewind_handler_with_scm
takes care that
data is protected from garbage collection.
void
scm_dynwind_free (void *mem)
¶Arrange for mem to be freed automatically whenever the current
context is exited, whether normally or non-locally.
scm_dynwind_free (mem)
is an equivalent shorthand for
scm_dynwind_unwind_handler (free, mem, SCM_F_WIND_EXPLICITLY)
.