unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: Marius Vollmer <mvo@zagadka.de>
Cc: guile-devel@gnu.org
Subject: Re: Stack unwinding for C code
Date: Wed, 31 Dec 2003 01:10:29 +0100	[thread overview]
Message-ID: <87brppkft6.fsf@zagadka.ping.de> (raw)
In-Reply-To: <m3pte7p5oy.fsf@laruns.ossau.uklinux.net> (Neil Jerram's message of "29 Dec 2003 23:25:33 +0000")

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


  reply	other threads:[~2003-12-31  0:10 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87brppkft6.fsf@zagadka.ping.de \
    --to=mvo@zagadka.de \
    --cc=guile-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).