guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Stack unwinding for C code


From: Marius Vollmer
Subject: Re: Stack unwinding for C code
Date: Wed, 31 Dec 2003 01:10:29 +0100
User-agent: Gnus/5.1002 (Gnus v5.10.2) Emacs/21.3 (gnu/linux)

Neil Jerram <address@hidden> writes:

> This all sounds fine, but I think doesn't address the point that I was
> trying to make.

Yep, I got carried away, sorry.

> My point was that it might be nice to extend your proposed API so
> that it could cover the other tricky points in writing Guile C code,
> some of which I listed above.

I see.  Hmm.  I have no ideas (yet) about what to do about
SCM_DEFER_INTS, etc.  But you are right, we should have a plan for
bringing all things that are related to the dynamic context together.
So what about separating the API further: there would be only
scm_begin_frame and an additional function scm_prevent_rewind (or
scm_prevent_reentry?) could be used like

    scm_begin_frame ();
    scm_prevent_rewind ();
    ...
    scm_end_frame ();

Additional things like a label, halting all other threads, etc, could
be done in a similar fashion, later.


This will create a new way to do dynamically scoped things from C.
For example, the scm_c_call_with_blocked_asyncs functionality could be
redone as

    scm_begin_frame ();
    scm_block_asyncs ();
    ...
    scm_end_frame ();

I think we should ideally offer only one variant, for consistency.
Hmm.  scm_c_call_with_blocked_asyncs is easier to use in the sense
that you can't get it wrong.  scm_begin_frame, scm_end_frame is easier
since you don't need to write a callback function and funnel all your
state thru a void pointer.

I'm feeling uneasy about all this since it seems gratuitous to
flip/flop the API style around so much.  But you just can't tell
people to use scm_internal_dynamic_wind to register their cleanup
actions.  That's too much work and feels too heavy; users probably
wont do it by default.  (I know I don't.)

(I hope I'm still addressing your point, Neil... ;-)


So, what do you all say?  Do we like the frame stuff enough to make it
our new default way of handling all dynamically scoped stuff in C?
Are we sure that users will use it correctly?

I have attached an updated proposal.

I do have an implementation ready and am about to commit it, so people
can try it out.  We also need to think about how to integrate it with
C++ exceptions.

> [...] Do you think there is existing C code that uses
> scm_c_define_gsubr and uses rewindability, such that we couldn't
> just change scm_c_define_gsubr to enforce non-rewindability?

Yes, I think so.  Functions that only work with SCM objects should be
safe by default.

> Hoping these comments help ...

Of course they do! :-)


Frames
------

Error reporting differs between C and Scheme: C functions usually
return some kind of indication to their calling function when an error
occured.  Scheme functions (including functions written in C that
follow the Scheme conventions) on the other hand usually do not return
to their caller in case of an error but instead transfer control to
some handler 'up the stack'.

Such a non-local transfer of control is called "unwinding the stack"
(and is implemented with longjmp).

Sometimes, it is necessary to perform cleanups when the unwinding
happens.  Frequently, dynamically allocated temporary data structures
need to be deallocated, for example.

Scheme code can use 'dynamic-wind' to register such cleanup actions.
C code could use it as well, but it would require a lot of code that
would be unnatural for C.  Therefore, Guile offers a facility that is
better suited for C code.

Guile offers the functions scm_begin_frame and scm_end_frame to
delimit a dynamic extent.  Within this dynamic extent, which is calles
a 'frame', you can perform various 'frame actions' that control what
happens when the frame is entered or left.  For example, you can
register a cleanup routine with scm_on_unwind that is executed when
the frame is left.

Here is an example.  Reference documentation is below.

  /* 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_begin_frame ();

    c_s1 = scm_to_string (s1);
    scm_on_unwind (free, s1, 1);

    c_s2 = scm_to_string (s2);
    scm_on_unwind (free, s2, 1);

    c_res = foo (c_s1, c_s2);
    if (c_res == NULL)
      scm_memory_error ("foo");

    scm_end_frame ();

    return scm_from_string (res, 1);
  }


- C Function: void scm_begin_frame (int flags)

  Starts a new frame and makes it the 'current' one.  FLAGS determines
  the default behavior of the frame.  For normal frames, use 0.  This
  will result in a frame that can not be reentered with a captured
  continuation.  See below.

  The frame is ended either implicitly when a non-local exit happens,
  or explicitly with scm_end_frame.

- C Function: void scm_on_unwind (void (*func)(void *), void *data,
                                  int explicit)

  Arranges for FUNC to be called with DATA as its arguments when the
  current frame ends implicitly.  If EXPLICIT is non-zero, FUNC is
  also called when the frame ends explicitly.

- C Function: void scm_end_frame ()

  End the current frame explicitly and make the previous frame
  current.


Continuations and frames
------------------------

In addition to unwinding the stack, Scheme can also 'rewind' it.  This
can happen when a continuation is called.  When a stack is rewound,
call frames are added back to it.  This can even happen multiple times
so that some code might find that a function returns more than once.
This is against the expectations of most C code.  For this reason, the
frames explained above do not allow the invocation of continuations
that would rewind those frames.

When you do want to allow continuations, you can use the
SCM_F_REWINDABLE_FRAME flag with scm_begin_frame.  In addition to
unwind actions, you can also register rewind actions for such frames
with scm_on_rewind.

- C Constant: SCM_F_REWINDABLE_FRAME

  When passed as a flag to scm_begin_frame, causes the frame to be
  'rewindable'.  This means that a continuation that has been captured
  during the frame is allowed to be called from its outside.

- C Function: void scm_on_rewind (void (*func)(void *), void *data,
                                  int explicit)

  Arrange for FUNC to be called with DATA as its argument when the
  current frame is restarted by rewinding the stack.  When EXPLICIT is
  non-zero, FUNC is called immediately as well.

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405




reply via email to

[Prev in Thread] Current Thread [Next in Thread]