From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Marius Vollmer Newsgroups: gmane.lisp.guile.devel Subject: Stack unwinding for C code Date: Fri, 26 Dec 2003 22:36:31 +0100 Sender: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Message-ID: <87y8sz5kio.fsf@zagadka.ping.de> NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1072474791 11861 80.91.224.253 (26 Dec 2003 21:39:51 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Fri, 26 Dec 2003 21:39:51 +0000 (UTC) Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Fri Dec 26 22:39:47 2003 Return-path: Original-Received: from monty-python.gnu.org ([199.232.76.173]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1AZzgR-0002Bq-00 for ; Fri, 26 Dec 2003 22:39:47 +0100 Original-Received: from localhost ([127.0.0.1] helo=monty-python.gnu.org) by monty-python.gnu.org with esmtp (Exim 4.24) id 1Aa0dD-0004CN-OF for guile-devel@m.gmane.org; Fri, 26 Dec 2003 17:40:31 -0500 Original-Received: from list by monty-python.gnu.org with tmda-scanned (Exim 4.24) id 1Aa0cn-0004C8-7C for guile-devel@gnu.org; Fri, 26 Dec 2003 17:40:05 -0500 Original-Received: from mail by monty-python.gnu.org with spam-scanned (Exim 4.24) id 1Aa0cH-0003iv-6e for guile-devel@gnu.org; Fri, 26 Dec 2003 17:40:04 -0500 Original-Received: from [199.232.41.8] (helo=mx20.gnu.org) by monty-python.gnu.org with esmtp (TLSv1:DES-CBC3-SHA:168) (Exim 4.24) id 1Aa0cG-0003i7-UG for guile-devel@gnu.org; Fri, 26 Dec 2003 17:39:32 -0500 Original-Received: from [195.253.8.218] (helo=mail.dokom.net) by mx20.gnu.org with esmtp (Exim 4.24) id 1AZzdO-0006Pw-Df for guile-devel@gnu.org; Fri, 26 Dec 2003 16:36:38 -0500 Original-Received: from dialin.speedway15.dip163.dokom.de ([195.253.15.163] helo=zagadka.ping.de) by mail.dokom.net with smtp (Exim 3.36 #3) id 1AZzdk-0005Zp-00 for guile-devel@gnu.org; Fri, 26 Dec 2003 22:37:00 +0100 Original-Received: (qmail 7363 invoked by uid 1000); 26 Dec 2003 21:36:31 -0000 Original-To: guile-devel@gnu.org User-Agent: Gnus/5.1002 (Gnus v5.10.2) Emacs/21.3 (gnu/linux) X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.2 Precedence: list List-Id: Developers list for Guile, the GNU extensibility library List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Xref: main.gmane.org gmane.lisp.guile.devel:3134 X-Report-Spam: http://spam.gmane.org/gmane.lisp.guile.devel:3134 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