Next: Handling Errors, Previous: Error Reporting, Up: Control Mechanisms
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 calles 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_memory_error ("foo"); 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 invokation of a continuation). The default is that a dynwind context can not be reentered non-locally.
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-reenterable 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 callscm_dynwind_end
for eachscm_dynwind_begin
, the behavior is undefined.
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
andscm_dynwind_rewind_handler
. The flags are listed in the following table.
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 withscm_dynwind_end
.The function
scm_dynwind_unwind_handler_with_scm
takes care that data is protected from garbage collection.
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.