6.11.10 Dynamic Wind

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);
}
Scheme Procedure: dynamic-wind in_guard thunk out_guard
C Function: scm_dynamic_wind (in_guard, thunk, out_guard)

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
C Type: scm_t_dynwind_flags

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.

C Function: 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.

C Function: void scm_dynwind_end ()

End the current dynamic context explicitly and make the previous one current.

C Type: scm_t_wind_flags

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.

C Function: void scm_dynwind_unwind_handler (void (*func)(void *), void *data, scm_t_wind_flags flags)
C Function: 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.

C Function: void scm_dynwind_rewind_handler (void (*func)(void *), void *data, scm_t_wind_flags flags)
C Function: 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.

C Function: 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).