unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: Marius Vollmer <mvo@zagadka.de>
Subject: Stack unwinding for C code
Date: Fri, 26 Dec 2003 22:36:31 +0100	[thread overview]
Message-ID: <87y8sz5kio.fsf@zagadka.ping.de> (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


             reply	other threads:[~2003-12-26 21:36 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-12-26 21:36 Marius Vollmer [this message]
2003-12-27  9:53 ` Stack unwinding for C code 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

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=87y8sz5kio.fsf@zagadka.ping.de \
    --to=mvo@zagadka.de \
    /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).