unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* Stack unwinding for C code
@ 2003-12-26 21:36 Marius Vollmer
  2003-12-27  9:53 ` tomas
  2003-12-27 12:11 ` Neil Jerram
  0 siblings, 2 replies; 16+ messages in thread
From: Marius Vollmer @ 2003-12-26 21:36 UTC (permalink / raw)


Hi,

I'm slowly making progress with replacing the gh_ interface and I
found that we first need to have good support for dealing with
non-local exits in the middle of allocating some temporary data
structures in order to prevent memory leaks.

We could either prevent non-local exits for a region of code, or allow
them and register cleanup actions.  Preventing them is difficult since
since you either need to avoid calling almost all libguile functions
in that region (which is very restrictive) or make sure that no error
actually happens (which is very difficult in a multi-threaded
program).

The proposal below therefore addresses the registerin of cleanup
actions, much like dynwind but in a more C friendly way.

Comments?  (I hope to implement most of it in the next days...)


Unwinding C call frames
-----------------------

Error reporting differes 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 'dynwind' 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.

These frames also inhibit continuations (as explained below).  Thus,
you might want to use frames even when you don't have any cleanup
actions to perform.  Also, the label that appears in a backtrace is
useful on it own as well.

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 occured 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 ("foo");
      {
        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 (const char *label)

  Starts a new frame and makes it the 'current' one.  The frame will
  be 'non-continueable', see below.

  The given LABEL will appear in backtraces and error messages.

  The frame is ended either implicitely when a non-local exit happens,
  or explicitely 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 implicitely.  When EXPLICITElY is non-zero, FUNC
  is also called when the frame ends explicitely.

- C Function: void scm_end_frame ()

  End the current frame explicitely 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 expections of most C code.  For this reason, the
frames explained above do not allow the construction or invokation of
continuations while they are active.  Thus, a frame ends exactly once,
either implicetely or explicitely.

When you do want to allow continuations, you can start a frame with
scm_begin_continuable_frame instead of with scm_begin_frame.  In
addition to unwind actions, you can also register rewind actions for
such frames with scm_on_rewind.

- C Function: void scm_begin_frame (const char *label)

  Starts a new frame and makes it the 'current' one.  The frame will
  be 'continueable', that is, it allows continuations to be created
  and invoked while it is active.

- 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.


Related stuff
-------------

- C Function: void scm_c_define_proc (const char *name,
                                      int req, int opt, int restp,
				      SCM (*proc) (...))

  The same as scm_c_define_gsubr but automatically begins and ends a
  non-continuable frame around the call to PROC.  The label of the
  frame is NAME.


Implementation
--------------

The frames stuff above can and probably should be the basis for all
the dynwind business of Guile, with scm_dynwind, for example, being
implemented using scm_begin_continuable_frame, scm_on_rewind,
scm_on_unwind, etc.

Also, the frames stored by the debugging evaluator could be combined
with this.

All data for frames could ba allocated on the stack, but the above API
does not allow this.  This has been done to achieve easy binary
compatibility and a very clean and simple API.


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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-26 21:36 Stack unwinding for C code Marius Vollmer
@ 2003-12-27  9:53 ` tomas
  2003-12-27 12:11 ` Neil Jerram
  1 sibling, 0 replies; 16+ messages in thread
From: tomas @ 2003-12-27  9:53 UTC (permalink / raw)
  Cc: guile-devel

On Fri, Dec 26, 2003 at 10:36:31PM +0100, Marius Vollmer wrote:
> Hi,
> 
> I'm slowly making progress with replacing the gh_ [...]

> Comments?  (I hope to implement most of it in the next days...)

Nice interface. I really like it. The best Christmas present
to date :-)

One typo nit: s/invokation/invocation/

Thanks

-- tomás


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-26 21:36 Stack unwinding for C code Marius Vollmer
  2003-12-27  9:53 ` tomas
@ 2003-12-27 12:11 ` Neil Jerram
  2003-12-27 17:37   ` Marius Vollmer
  1 sibling, 1 reply; 16+ messages in thread
From: Neil Jerram @ 2003-12-27 12:11 UTC (permalink / raw)
  Cc: guile-devel

>>>>> "Marius" == Marius Vollmer <mvo@zagadka.de> writes:

    Marius> The proposal below therefore addresses the registerin of cleanup
    Marius> actions, much like dynwind but in a more C friendly way.

    Marius> Comments?  (I hope to implement most of it in the next days...)

Hi Marius,

This looks nice - I'll take your word for it that we need this, and
the API looks clean as you say.  A few comments and typo corrections,
though ...

    Marius> Error reporting differes between C and Scheme: C functions usually
    Marius> return some kind of indication to their calling function when an error
    Marius> occured.  Scheme functions (including functions written in C that

typo - "occurred"

    Marius> These frames also inhibit continuations (as explained
    Marius> below). [...]

In API terms, this feels to me like the same area as issues like
SCM_DEFER_INTS, control of signal delivery, inhibiting thread
switching, etc.  I realize that these are quite different mechanisms
really, but it would be lovely if the API that you are proposing here
could also cope with and document all those other areas once and for
all.

    Marius>     scm_begin_frame ("foo");
    Marius>       {

Is this block necessary? - it looks a little ugly to me.

    Marius>   Starts a new frame and makes it the 'current' one.  The frame will
    Marius>   be 'non-continueable', see below.

typo - "continuable"

Also differs - perhaps confusingly - from my use of "continuable" for
a stack in (ice-9 debugger).  (My use of "continuable" is to describe
a stack at a breakpoint, where execution can continue, as opposed to
the-last-stack after an error, where execution cannot continue.

Perhaps "continuation-proof" or "continuation-protected"?

    Marius>   The given LABEL will appear in backtraces and error messages.

How (out of interest)?

    Marius>   The frame is ended either implicitely when a non-local exit happens,
    Marius>   or explicitely with scm_end_frame.

typos - "implicitly", "explicitly"

    Marius>   Arranges for FUNC to be called with DATA as its arguments when the
    Marius>   current frame ends implicitely.  When EXPLICITElY is non-zero, FUNC
    Marius>   is also called when the frame ends explicitely.

"If" sounds better than "When" here, I'd say.

    Marius> This is against the expections of most C code.  For this reason, the
    Marius> frames explained above do not allow the construction or invokation of

typos - "expectations", "invocation"

    Marius> - C Function: void scm_c_define_proc (const char *name,
    Marius>                                       int req, int opt, int restp,
    Marius> 				      SCM (*proc) (...))

This is not a good name: it is not helpful to suggest that "proc" =
"framed gsubr".  How about scm_c_define_framed_gsubr ?

    Marius> Also, the frames stored by the debugging evaluator could be combined
    Marius> with this.

Interesting.  Do you propose to do this as part of your initial work?

Regards,
        Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-27 12:11 ` Neil Jerram
@ 2003-12-27 17:37   ` Marius Vollmer
  2003-12-28  2:25     ` Tom Lord
  2003-12-29 23:25     ` Neil Jerram
  0 siblings, 2 replies; 16+ messages in thread
From: Marius Vollmer @ 2003-12-27 17:37 UTC (permalink / raw)
  Cc: guile-devel

Neil Jerram <neil@ossau.uklinux.net> writes:

> This looks nice - I'll take your word for it that we need this, and
> the API looks clean as you say.

Don't you think we need it?  Maybe I'm overlooking some obvious
alternative that would work just as well...

The example shows what I want to achieve: during a sequence of
conversions from SCM objects to C data structures, we will frequently
have the case that an earlier conversion has allocated some resources
that need to be freed later on.  However a later conversion might want
to throw an error and the cleanup needs to happen then.

First, we (on #guile) tried some approaches that would prevent the
throwing of errors.  For example, we could separate type checking and
conversion, ala

    scm_check_string (s1);
    scm_check_string (s2);

    /* Now the conversion functions are guaranteed not to throw. */

    c_s1 = scm_to_string (s1);
    c_s2 = scm_to_string (s2);

But this leads to problems in a multi-threaded program: when you have
composite SCM types such as a list, you must make absolutely sure that
the object doesn't change from checking to conversion.  You need to
stop all other guile threads during the conversion sequence, which is
at least as costly as having this unwind machinery, I think.

> A few comments and typo corrections,
> though ...

Thanks, hopefully the poor form didn't distract from the content...

>     Marius> These frames also inhibit continuations (as explained
>     Marius> below). [...]
>
> In API terms, this feels to me like the same area as issues like
> SCM_DEFER_INTS, control of signal delivery, inhibiting thread
> switching, etc.  I realize that these are quite different mechanisms
> really, but it would be lovely if the API that you are proposing here
> could also cope with and document all those other areas once and for
> all.

Hmm.  I had to change the continuation stuff already: just disallowing
all construction and invocation of continuations is too restricitve:
continuations that are only used below the frame cause no harm.  Only
continuations that cause the frame to be rewound need to be prevented.
This is too restrictive as well, in a sense, since continuations that
are only used in a suspend/resume way (for threads or coroutines, say)
are OK as well.  But the general continuations are too general to know
how they are going to be used, so throwing an error when such a frame
is rewound is all we can do.

>     Marius>     scm_begin_frame ("foo");
>     Marius>       {
>
> Is this block necessary? - it looks a little ugly to me.

No, scm_begin_frame is a normal function, nothing special about it.

>     Marius>   Starts a new frame and makes it the 'current' one.
>     Marius>   The frame will be 'non-continueable', see below.
>
> typo - "continuable"

Yeah, thanks. :-) I knew it was wrong, but I couldn't figure out what
was right.  Ispell doesn't know about "continuable"...

> Also differs - perhaps confusingly - from my use of "continuable" for
> a stack in (ice-9 debugger).  (My use of "continuable" is to describe
> a stack at a breakpoint, where execution can continue, as opposed to
> the-last-stack after an error, where execution cannot continue.

Yes, the term isn't very good for what I use it for.

> Perhaps "continuation-proof" or "continuation-protected"?

What about "rewindable"?

>     Marius>   The given LABEL will appear in backtraces and error messages.
>
> How (out of interest)?

Don't know yet! :-) Also, I'm no longer sure that it is a good idea to
combine the backtrace stuff with the frame stuff.  I think I'll remove
the label.

(I think that ultimately, we should have a different way of reporting
error from C code: we should not include the subr name in the call to
the error reporting function but rather rely on a more general
mechanism for providing a backtrace for C code.  I found it attractive
to just combine it with the frame stuff, but it probably is too
early...)

>
>     Marius>   The frame is ended either implicitely when a non-local
>     Marius>   exit happens, or explicitely with scm_end_frame.
>
> typos - "implicitly", "explicitly"

Oops, I do that all the time...

>     Marius>   Arranges for FUNC to be called with DATA as its
>     Marius>   arguments when the current frame ends implicitely.
>     Marius>   When EXPLICITElY is non-zero, FUNC is also called when
>     Marius>   the frame ends explicitely.
>
> "If" sounds better than "When" here, I'd say.

Ok.

>     Marius> - C Function: void scm_c_define_proc (const char *name,
>     Marius>                                       int req, int opt,
>     Marius>                                       int restp,
>     Marius> 				            SCM (*proc) (...))
>
> This is not a good name: it is not helpful to suggest that "proc" =
> "framed gsubr".  How about scm_c_define_framed_gsubr ?

Hmm.  That is so technical.  I wanted to imply that scm_c_define_proc
is the usual way to export C procedures to Scheme.  Even if you don't
explicitly ;) ask for a frame, you still get it since it is good for
you....

But, yeah, I'm removing this together with the label stuff.

>     Marius> Also, the frames stored by the debugging evaluator could
>     Marius> be combined with this.
>
> Interesting.  Do you propose to do this as part of your initial work?

Well, no.  They are too different after all.  It is very nice how
efficient the debugging frames currently are, and I wouldn't want to
destroy that.

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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-27 17:37   ` Marius Vollmer
@ 2003-12-28  2:25     ` Tom Lord
  2003-12-29 22:12       ` Marius Vollmer
  2003-12-29 23:25     ` Neil Jerram
  1 sibling, 1 reply; 16+ messages in thread
From: Tom Lord @ 2003-12-28  2:25 UTC (permalink / raw)
  Cc: guile-devel, neil


    > From: Marius Vollmer <mvo@zagadka.de>

    > First, we (on #guile) tried some approaches that would prevent the
    > throwing of errors.  


Did you consider simply using error-codes as return values?

-t



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-28  2:25     ` Tom Lord
@ 2003-12-29 22:12       ` Marius Vollmer
  0 siblings, 0 replies; 16+ messages in thread
From: Marius Vollmer @ 2003-12-29 22:12 UTC (permalink / raw)
  Cc: neil, guile-devel

Tom Lord <lord@emf.net> writes:

>     > From: Marius Vollmer <mvo@zagadka.de>
>
>     > First, we (on #guile) tried some approaches that would prevent the
>     > throwing of errors.  
>
>
> Did you consider simply using error-codes as return values?

Yes.  That would create two different styles of libguile functions,
and you wouldn't be able to use the functions that throw errors in
situations were you have cleanups pending.  That would be too
restricing I think.

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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-27 17:37   ` Marius Vollmer
  2003-12-28  2:25     ` Tom Lord
@ 2003-12-29 23:25     ` Neil Jerram
  2003-12-31  0:10       ` Marius Vollmer
  1 sibling, 1 reply; 16+ messages in thread
From: Neil Jerram @ 2003-12-29 23:25 UTC (permalink / raw)
  Cc: guile-devel

>>>>> "Marius" == Marius Vollmer <mvo@zagadka.de> writes:

    Marius> Neil Jerram <neil@ossau.uklinux.net> writes:
    >> This looks nice - I'll take your word for it that we need this, and
    >> the API looks clean as you say.

    Marius> Don't you think we need it?  Maybe I'm overlooking some obvious
    Marius> alternative that would work just as well...

Sorry - I didn't mean that at all.  I meant only that I hadn't
bothered to think so carefully about the requirement as about your
proposal to meet it.  I am happy that this is a real requirement.

    >> In API terms, this feels to me like the same area as issues like
    >> SCM_DEFER_INTS, control of signal delivery, inhibiting thread
    >> switching, etc.  I realize that these are quite different mechanisms
    >> really, but it would be lovely if the API that you are proposing here
    >> could also cope with and document all those other areas once and for
    >> all.

    Marius> Hmm.  I had to change the continuation stuff already: just disallowing
    Marius> all construction and invocation of continuations is too restricitve:
    Marius> continuations that are only used below the frame cause no harm.  Only
    Marius> continuations that cause the frame to be rewound need to be prevented.
    Marius> This is too restrictive as well, in a sense, since continuations that
    Marius> are only used in a suspend/resume way (for threads or coroutines, say)
    Marius> are OK as well.  But the general continuations are too general to know
    Marius> how they are going to be used, so throwing an error when such a frame
    Marius> is rewound is all we can do.

This all sounds fine, but I think doesn't address the point that I was
trying to make.  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.

    >> Perhaps "continuation-proof" or "continuation-protected"?

    Marius> What about "rewindable"?

Yes!  "rewindable" is excellent.

    Marius> (I think that ultimately, we should have a different way of reporting
    Marius> error from C code: we should not include the subr name in the call to
    Marius> the error reporting function but rather rely on a more general
    Marius> mechanism for providing a backtrace for C code.  I found it attractive
    Marius> to just combine it with the frame stuff, but it probably is too
    Marius> early...)

You're probably right, but to be sure I suspect we need more
experiences from users using Guile's debugging tools first.  I doubt
it's worth playing with this until we know more about what is really
useful.

    Marius> - C Function: void scm_c_define_proc (const char *name,
    Marius> int req, int opt,
    Marius> int restp,
    Marius> SCM (*proc) (...))
    >> 
    >> This is not a good name: it is not helpful to suggest that "proc" =
    >> "framed gsubr".  How about scm_c_define_framed_gsubr ?

    Marius> Hmm.  That is so technical.  I wanted to imply that scm_c_define_proc
    Marius> is the usual way to export C procedures to Scheme.  Even if you don't
    Marius> explicitly ;) ask for a frame, you still get it since it is good for
    Marius> you....

    Marius> But, yeah, I'm removing this together with the label stuff.

I don't understand.  It could still be useful for C procedures to be
non-rewindable by default, even if there isn't a label associated with
the framing.  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?

Hoping these comments help ...

        Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-29 23:25     ` Neil Jerram
@ 2003-12-31  0:10       ` Marius Vollmer
  2004-01-02 11:45         ` Dirk Herrmann
                           ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Marius Vollmer @ 2003-12-31  0:10 UTC (permalink / raw)
  Cc: guile-devel

Neil Jerram <neil@ossau.uklinux.net> 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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-31  0:10       ` Marius Vollmer
@ 2004-01-02 11:45         ` Dirk Herrmann
  2004-01-02 17:38           ` Marius Vollmer
  2004-01-06 18:37         ` Paul Jarc
  2004-01-13 17:24         ` Rob Browning
  2 siblings, 1 reply; 16+ messages in thread
From: Dirk Herrmann @ 2004-01-02 11:45 UTC (permalink / raw)
  Cc: guile-devel, Neil Jerram

Marius Vollmer wrote:

>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 ();
>
I like this style of interface for its simplicity. But, I am somewhat 
confused since in your proposal below you don't suggest this style of 
interface, but instead describe scm_begin_frame as receiving an 
additional argument with flags. I wouldn't prefer any of the two 
solutions, but I am currently not sure what you actually suggest - 
especially since in the example given below you don't pass any argument 
to scm_begin_frame.

>Sometimes, it is necessary to perform cleanups when the unwinding
>happens.  Frequently, dynamically allocated temporary data structures
>need to be deallocated, for example.
>
If this document was to be reused in the documentation later, we should 
given an example here.

>- 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.
>
If this style of API is used (that is, passing a 'flags' argument to 
scm_begin_frame instead of having separate functions like 
scm_prevent_rewind and similar), then I suggest to use an enumeration 
type with all possible flags instead of an int type. This improves both 
type safety and readability of the code using scm_begin_frame. The same 
applies to the 'explicit' argument to scm_on_unwind and scm_on_rewind.

>- 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.
>
It is a nice coincidence that 'free' matches the void (*func) (void *) 
signature, especially since free will probably be one of the most 
frequently used functions with scm_on_unwind. fclose, however, does not 
match and is another candidate that may be commonly used. Unfortunately 
it wouldn't be standard conforming to just cast fclose to match the 
signature. I suggest that (in addition to the generic scm_on_unwind) for 
a limited set of common functions we provide specialized 
scm_on_unwind_xxx functions, like:

scm_on_unwind_free (void *data, int explicit); // could simply be 
#defined to scm_on_unwind
scm_on_unwind_fclose (FILE* fp, int explicit); // on some architectures 
this may also safely be #defined to scm_on_unwind
// maybe there are other typical cleanup functions...

Then, scm_on_unwind_free could either simply be #defined to 
scm_on_unwind, or - if it brings some performance and code size benefit 
to avoid passing the additional argument - provided as a special 
implementation. On some architectures it may also be an option to just 
#define scm_on_unwind_fclose to scm_on_unwind.

Best regards
Dirk Herrmann



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2004-01-02 11:45         ` Dirk Herrmann
@ 2004-01-02 17:38           ` Marius Vollmer
  2004-01-03 22:08             ` Marius Vollmer
  2004-01-10 11:45             ` Dirk Herrmann
  0 siblings, 2 replies; 16+ messages in thread
From: Marius Vollmer @ 2004-01-02 17:38 UTC (permalink / raw)
  Cc: guile-devel, Neil Jerram

Dirk Herrmann <dirk@dirk-herrmanns-seiten.de> writes:

> I like this style of interface for its simplicity.

Ok!  Given all the feedback, I'll design/document it as the general
mechanism for dealing with dynamic extents from C.

> But, I am somewhat confused since in your proposal below you don't
> suggest this style of interface, but instead describe
> scm_begin_frame as receiving an additional argument with flags.

Yes, that was confusing, sorry.

> I wouldn't prefer any of the two solutions, but I am currently not
> sure what you actually suggest - especially since in the example
> given below you don't pass any argument to scm_begin_frame.

The first variant (with scm_prevent_rewind) would be more elegant from
an implementational point of view.  The latter (with
SCM_F_REWINDABLE_FRAME) leads to a more desirable default behavior.  I
think people should explicitely allow rewinding when they have unwind
handlers.

So, I prefer the latter variant.

>>- 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.
>>
> If this style of API is used (that is, passing a 'flags' argument to
> scm_begin_frame instead of having separate functions like
> scm_prevent_rewind and similar), then I suggest to use an enumeration
> type with all possible flags instead of an int type. This improves
> both type safety and readability of the code using
> scm_begin_frame. The same applies to the 'explicit' argument to
> scm_on_unwind and scm_on_rewind.

Yep, agreed.

> It is a nice coincidence that 'free' matches the void (*func) (void
> *) signature, especially since free will probably be one of the most
> frequently used functions with scm_on_unwind. fclose, however, does
> not match and is another candidate that may be commonly
> used. Unfortunately it wouldn't be standard conforming to just cast
> fclose to match the signature.

Is that a theoretical problem or do indeed platforms exist where you
can't cast fclose to (void (*)(void *))?

If it is only theoretical, I'm inclined not to worry about it...

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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2004-01-02 17:38           ` Marius Vollmer
@ 2004-01-03 22:08             ` Marius Vollmer
  2004-01-10 11:45             ` Dirk Herrmann
  1 sibling, 0 replies; 16+ messages in thread
From: Marius Vollmer @ 2004-01-03 22:08 UTC (permalink / raw)
  Cc: Neil Jerram, guile-devel

Marius Vollmer <mvo@zagadka.de> writes:

> Ok!  Given all the feedback, I'll design/document it as the general
> mechanism for dealing with dynamic extents from C.

Done, please give it a try!

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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-31  0:10       ` Marius Vollmer
  2004-01-02 11:45         ` Dirk Herrmann
@ 2004-01-06 18:37         ` Paul Jarc
  2004-01-07 20:27           ` Marius Vollmer
  2004-01-13 17:24         ` Rob Browning
  2 siblings, 1 reply; 16+ messages in thread
From: Paul Jarc @ 2004-01-06 18:37 UTC (permalink / raw)
  Cc: guile-devel, Neil Jerram

Marius Vollmer <mvo@zagadka.de> wrote:
>     c_s1 = scm_to_string (s1);
>     scm_on_unwind (free, s1, 1);
>
>     c_s2 = scm_to_string (s2);
>     scm_on_unwind (free, s2, 1);

Nit: shouldn't you be passing c_s1 and c_s2 instead of s1 and s2 to
scm_on_unwind?


paul


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2004-01-06 18:37         ` Paul Jarc
@ 2004-01-07 20:27           ` Marius Vollmer
  0 siblings, 0 replies; 16+ messages in thread
From: Marius Vollmer @ 2004-01-07 20:27 UTC (permalink / raw)
  Cc: guile-devel

prj@po.cwru.edu (Paul Jarc) writes:

> Nit: shouldn't you be passing c_s1 and c_s2 instead of s1 and s2 to
> scm_on_unwind?

Yes, thanks!


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2004-01-02 17:38           ` Marius Vollmer
  2004-01-03 22:08             ` Marius Vollmer
@ 2004-01-10 11:45             ` Dirk Herrmann
  2004-01-11  1:23               ` Marius Vollmer
  1 sibling, 1 reply; 16+ messages in thread
From: Dirk Herrmann @ 2004-01-10 11:45 UTC (permalink / raw)
  Cc: guile-devel, Neil Jerram

Marius Vollmer wrote:

>Dirk Herrmann <dirk@dirk-herrmanns-seiten.de> writes:
>  
>
>>I wouldn't prefer any of the two solutions, but I am currently not
>>sure what you actually suggest - especially since in the example
>>given below you don't pass any argument to scm_begin_frame.
>>    
>>
>
>The first variant (with scm_prevent_rewind) would be more elegant from
>an implementational point of view.  The latter (with
>SCM_F_REWINDABLE_FRAME) leads to a more desirable default behavior.  I
>think people should explicitely allow rewinding when they have unwind
>handlers.
>
Hmm, probably I am missing something, but wouldn't it just be possible 
to make the non-rewindable frame the default, and offer scm_allow_rewind 
as a function then?

>>It is a nice coincidence that 'free' matches the void (*func) (void
>>*) signature, especially since free will probably be one of the most
>>frequently used functions with scm_on_unwind. fclose, however, does
>>not match and is another candidate that may be commonly
>>used. Unfortunately it wouldn't be standard conforming to just cast
>>fclose to match the signature.
>>    
>>
>
>Is that a theoretical problem or do indeed platforms exist where you
>can't cast fclose to (void (*)(void *))?
>
>If it is only theoretical, I'm inclined not to worry about it...
>  
>
Huh, such casts would need to be added explicitly, both in guile and in 
user code. I wouldn't recommend users to use such a programming style. 
What we do within guile is a different thing, but even here I am much in 
favor of having our code as standard conforming as possible - or at 
least encapsulate places where it isn't. Maybe we should first wait what 
kinds of cleanup functions we detect to be useful within guile and then 
decide about this issue.

Best regards
Dirk



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2004-01-10 11:45             ` Dirk Herrmann
@ 2004-01-11  1:23               ` Marius Vollmer
  0 siblings, 0 replies; 16+ messages in thread
From: Marius Vollmer @ 2004-01-11  1:23 UTC (permalink / raw)
  Cc: guile-devel, Neil Jerram

Dirk Herrmann <dirk@dirk-herrmanns-seiten.de> writes:

>>The first variant (with scm_prevent_rewind) would be more elegant from
>>an implementational point of view.  The latter (with
>>SCM_F_REWINDABLE_FRAME) leads to a more desirable default behavior.  I
>>think people should explicitely allow rewinding when they have unwind
>>handlers.
>>
> Hmm, probably I am missing something, but wouldn't it just be possible
> to make the non-rewindable frame the default, and offer
> scm_allow_rewind as a function then?

Yes, that would be possible, but the flags variant is OK, too, no?

> [...] Maybe we should first wait what kinds of cleanup functions we
> detect to be useful within guile and then decide about this issue.

Yes.

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


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Stack unwinding for C code
  2003-12-31  0:10       ` Marius Vollmer
  2004-01-02 11:45         ` Dirk Herrmann
  2004-01-06 18:37         ` Paul Jarc
@ 2004-01-13 17:24         ` Rob Browning
  2 siblings, 0 replies; 16+ messages in thread
From: Rob Browning @ 2004-01-13 17:24 UTC (permalink / raw)
  Cc: guile-devel, Neil Jerram

Marius Vollmer <mvo@zagadka.de> writes:

> 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?

A bit belated, I know, but it seems like a good approach to me.

-- 
Rob Browning
rlb @defaultvalue.org and @debian.org; previously @cs.utexas.edu
GPG starting 2002-11-03 = 14DD 432F AE39 534D B592  F9A0 25C8 D377 8C7E 73A4


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2004-01-13 17:24 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-12-26 21:36 Stack unwinding for C code Marius Vollmer
2003-12-27  9:53 ` tomas
2003-12-27 12:11 ` Neil Jerram
2003-12-27 17:37   ` Marius Vollmer
2003-12-28  2:25     ` Tom Lord
2003-12-29 22:12       ` Marius Vollmer
2003-12-29 23:25     ` Neil Jerram
2003-12-31  0:10       ` Marius Vollmer
2004-01-02 11:45         ` Dirk Herrmann
2004-01-02 17:38           ` Marius Vollmer
2004-01-03 22:08             ` Marius Vollmer
2004-01-10 11:45             ` Dirk Herrmann
2004-01-11  1:23               ` Marius Vollmer
2004-01-06 18:37         ` Paul Jarc
2004-01-07 20:27           ` Marius Vollmer
2004-01-13 17:24         ` Rob Browning

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).