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: Re: Stack unwinding for C code Date: Wed, 31 Dec 2003 01:10:29 +0100 Sender: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Message-ID: <87brppkft6.fsf@zagadka.ping.de> References: <87y8sz5kio.fsf@zagadka.ping.de> <87ptea40wu.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 1072829673 31766 80.91.224.253 (31 Dec 2003 00:14:33 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Wed, 31 Dec 2003 00:14:33 +0000 (UTC) Cc: guile-devel@gnu.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Wed Dec 31 01:14:23 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 1AbU0E-0008C9-00 for ; Wed, 31 Dec 2003 01:14:22 +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 1AbUuu-00012I-Tu for guile-devel@m.gmane.org; Tue, 30 Dec 2003 20:12:56 -0500 Original-Received: from list by monty-python.gnu.org with tmda-scanned (Exim 4.24) id 1AbUuZ-0000yd-K2 for guile-devel@gnu.org; Tue, 30 Dec 2003 20:12:35 -0500 Original-Received: from mail by monty-python.gnu.org with spam-scanned (Exim 4.24) id 1AbUu3-0000ei-8Y for guile-devel@gnu.org; Tue, 30 Dec 2003 20:12:34 -0500 Original-Received: from [195.253.8.218] (helo=mail.dokom.net) by monty-python.gnu.org with esmtp (Exim 4.24) id 1AbUu2-0000ed-Jf for guile-devel@gnu.org; Tue, 30 Dec 2003 20:12:02 -0500 Original-Received: from dialin.speedway15.dip16.dokom.de ([195.253.15.16] helo=zagadka.ping.de) by mail.dokom.net with smtp (Exim 3.36 #3) id 1AbTx0-0005eU-00 for guile-devel@gnu.org; Wed, 31 Dec 2003 01:11:02 +0100 Original-Received: (qmail 10574 invoked by uid 1000); 31 Dec 2003 00:10:29 -0000 Original-To: Neil Jerram In-Reply-To: (Neil Jerram's message of "29 Dec 2003 23:25:33 +0000") 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:3146 X-Report-Spam: http://spam.gmane.org/gmane.lisp.guile.devel:3146 Neil Jerram 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