changeset 1920:c66036f59678

[xemacs-hg @ 2004-02-20 07:29:16 by stephent] GCPRO documentation <87y8qynrj0.fsf@tleepslib.sk.tsukuba.ac.jp>
author stephent
date Fri, 20 Feb 2004 07:29:23 +0000
parents 9bde73b8c020
children dd8c3a4ab510
files man/ChangeLog man/internals/internals.texi src/ChangeLog src/bytecode.c src/fns.c
diffstat 5 files changed, 101 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/man/ChangeLog	Thu Feb 19 22:50:36 2004 +0000
+++ b/man/ChangeLog	Fri Feb 20 07:29:23 2004 +0000
@@ -1,3 +1,8 @@
+2004-02-20  Stephen J. Turnbull  <stephen@xemacs.org>
+
+	* internals/internals.texi (GCPROing): Mention `Fsignal'.  Clarify
+	that references, not the objects themselves, are what is marked.
+
 2004-01-26  Stephen J. Turnbull  <stephen@xemacs.org>
 
 	* lispref/specifiers.texi (Copyright): Update.
--- a/man/internals/internals.texi	Thu Feb 19 22:50:36 2004 +0000
+++ b/man/internals/internals.texi	Fri Feb 20 07:29:23 2004 +0000
@@ -5943,20 +5943,37 @@
 frequently, there will be no other pointer to the object while the
 function is running, and if a garbage collection occurs and the object
 needs to be referenced again, bad things will happen.  The solution is
-to mark those objects with @code{GCPRO}.  Unfortunately this is easy to
-forget, and there is basically no way around this problem.  Here are
-some rules, though:
+to mark those references with @code{GCPRO}.  Note that it is a
+@emph{reference} that is marked with @code{GCPRO}, not an object.  If
+you declare a @code{Lisp_Object} variable, assign to it, @code{GCPRO}
+it, and then assign to it again, the first object assigned @emph{is not}
+protected, while the second object @emph{is} protected.  Unfortunately
+@code{GCPRO}ing is easy to forget, and there is basically no way around
+this problem.  Here are some rules, though:
 
 @enumerate
 @item
-For every @code{GCPRO@var{n}}, there have to be declarations of
-@code{struct gcpro gcpro1, gcpro2}, etc.
+A garbage collection can occur whenever anything calls @code{Feval}, or
+whenever a @code{QUIT} can occur where execution can continue past
+this. (Remember, this is almost anywhere.)  Note that @code{Fsignal} can
+GC, and it can return (even though it normally doesn't).  This means
+that you must @code{GCPRO} before calling most of the error functions,
+including the @samp{CONCHECK} family of macros, if references occur
+after the call.
 
 @item
 You @emph{must} @code{UNGCPRO} anything that's @code{GCPRO}ed, and you
 @emph{must not} @code{UNGCPRO} if you haven't @code{GCPRO}ed.  Getting
 either of these wrong will lead to crashes, often in completely random
-places unrelated to where the problem lies.
+places unrelated to where the problem lies.  There are some functions
+(@code{Fsignal} is the canonical example) which may or may not return.
+In these cases, the function is responsible for cleaning up the
+@code{GCPRO}s if it doesn't return, so you should treat it as an
+ordinary function.
+
+@item
+For every @code{GCPRO@var{n}}, there have to be declarations of
+@code{struct gcpro gcpro1, gcpro2, ..., gcpro@var{n}}.
 
 @item
 The way this actually works is that all currently active @code{GCPRO}s
@@ -6015,11 +6032,6 @@
 need to use the object.  Even then, consider @code{GCPRO}ing.
 
 @item
-A garbage collection can occur whenever anything calls @code{Feval}, or
-whenever a QUIT can occur where execution can continue past
-this. (Remember, this is almost anywhere.)
-
-@item
 If you have the @emph{least smidgeon of doubt} about whether
 you need to @code{GCPRO}, you should @code{GCPRO}.
 
--- a/src/ChangeLog	Thu Feb 19 22:50:36 2004 +0000
+++ b/src/ChangeLog	Fri Feb 20 07:29:23 2004 +0000
@@ -1,3 +1,17 @@
+2004-02-20  Stephen J. Turnbull  <stephen@xemacs.org>
+
+	* bytecode.c (execute_optimized_program):
+	Identify and mark more potential GC locations.
+	Remove dead STATIC_GCPRO_STACK code and CPP constructs.
+	Document GCPRO_STACK usage.
+
+	* fns.c (Fnthcdr):
+	(Fnth):
+	(Felt):
+	Propagate "This function can GC"s.
+	(Fput):
+	Propagate "This function cannot GC"s.
+
 2004-02-17  Jerry James  <james@xemacs.org>
 
 	* menubar-x.c (protected_menu_item_descriptor_to_widget_value_1):
--- a/src/bytecode.c	Thu Feb 19 22:50:36 2004 +0000
+++ b/src/bytecode.c	Fri Feb 20 07:29:23 2004 +0000
@@ -447,6 +447,7 @@
    but don't pop it. */
 #define TOP (*stack_ptr)
 
+/* See comment before the big switch in execute_optimized_program(). */
 #define GCPRO_STACK  (gcpro1.nvars = stack_ptr - stack_beg)
 
 /* The actual interpreter for byte code.
@@ -476,25 +477,40 @@
   Lisp_Object *stack_end = stack_beg + stack_depth;
 #endif
 
-#if STATIC_GCPRO_STACK
-  /* Initialize all the objects on the stack to Qnil,
-     so we can GCPRO the whole stack.
-     The first element of the stack is actually a dummy. */
-  {
-    int i;
-    Lisp_Object *p;
-    for (i = stack_depth, p = stack_ptr; i--;)
-      *++p = Qnil;
-  }
-#endif  
+  /* We used to GCPRO the whole interpreter stack before entering this while
+     loop (21.5.14 and before), but that interferes with collection of weakly
+     referenced objects.  Although strictly speaking there's no promise that
+     weak references will disappear by any given point in time, they should
+     be collected at the first opportunity.  Waiting until exit from the
+     function caused test failures because "stale" objects "above" the top of
+     the stack were still GCPROed, and they were not getting collected until
+     after exit from the (byte-compiled) test!
+
+     Now the idea is to dynamically adjust the array of GCPROed objects to
+     include only the "active" region of the stack.
+
+     We use the "GCPRO1 the array base and set the nvars member" method.  It
+     would be slightly inefficient but correct to use GCPRO1_ARRAY here.  It
+     would just redundantly set nvars.
+     #### Maybe it would be clearer to use GCPRO1_ARRAY and do GCPRO_STACK
+     after the switch?
+
+     GCPRO_STACK is something of a misnomer, because it suggests that a
+     struct gcpro is initialized each time.  This is false; only the nvars
+     member of a single struct gcpro is being adjusted.  This works because
+     each time a new object is assigned to a stack location, the old object
+     loses its reference and is effectively UNGCPROed, and the new object is
+     automatically GCPROed as long as nvars is correct.  Only when we
+     return from the interpreter do we need to finalize the struct gcpro
+     itself, and that's done at case Breturn.
+  */
   GCPRO1 (stack_ptr[1]);
-#if STATIC_GCPRO_STACK
-  gcpro1.nvars = stack_depth;
-#endif  
 
   while (1)
     {
       REGISTER Opcode opcode = (Opcode) READ_UINT_1;
+
+      GCPRO_STACK;		/* Get nvars right before maybe signaling. */
 #ifdef ERROR_CHECK_BYTE_CODE
       if (stack_ptr > stack_end)
 	stack_overflow ("byte code stack overflow", Qunbound);
@@ -508,9 +524,6 @@
       meter_code (prev_opcode, this_opcode);
 #endif
 
-#if !STATIC_GCPRO_STACK
-      GCPRO_STACK;
-#endif
       switch (opcode)
 	{
 	  REGISTER int n;
@@ -539,6 +552,8 @@
 	  Lisp_Object symbol = constants_data[n];
 	  Lisp_Object value = XSYMBOL (symbol)->value;
 	  if (SYMBOL_VALUE_MAGIC_P (value))
+	    /* I GCPRO_STACKed Fsymbol_value elsewhere, but I dunno why. */
+	    /* GCPRO_STACK; */
 	    value = Fsymbol_value (symbol);
 	  PUSH (value);
 	  break;
@@ -616,6 +631,8 @@
 	case Bcall+7:
 	  n = (opcode <  Bcall+6 ? opcode - Bcall :
 	       opcode == Bcall+6 ? READ_UINT_1 : READ_UINT_2);
+	  /* #### Shouldn't this be just before the Ffuncall?
+	     Neither Fget nor Fput can GC. */
 	  /* GCPRO_STACK; */
 	  DISCARD (n);
 #ifdef BYTE_CODE_METER
@@ -745,10 +762,14 @@
 	  break;
 
 	case Bcar:
+	  /* Fcar can GC via wrong_type_argument. */
+	  /* GCPRO_STACK; */
 	  TOP = CONSP (TOP) ? XCAR (TOP) : Fcar (TOP);
 	  break;
 
 	case Bcdr:
+	  /* Fcdr can GC via wrong_type_argument. */
+	  /* GCPRO_STACK; */
 	  TOP = CONSP (TOP) ? XCDR (TOP) : Fcdr (TOP);
 	  break;
 
@@ -762,6 +783,8 @@
 	case Bnth:
 	  {
 	    Lisp_Object arg = POP;
+	    /* Fcar and Fnthcdr can GC via wrong_type_argument. */
+	    /* GCPRO_STACK; */
 	    TOP = Fcar (Fnthcdr (TOP, arg));
 	    break;
 	  }
@@ -848,6 +871,8 @@
 	  n = READ_UINT_1;
 	do_concat:
 	  DISCARD (n - 1);
+	  /* Apparently `concat' can GC; Fconcat GCPROs its arguments. */
+	  /* GCPRO_STACK; */
 	  TOP = Fconcat (n, &TOP);
 	  break;
 
@@ -865,6 +890,7 @@
 	  }
 
 	case Bsymbol_value:
+	  /* Why does this need GCPRO_STACK?  If not, remove others, too. */
 	  /* GCPRO_STACK; */
 	  TOP = Fsymbol_value (TOP);
 	  break;
@@ -931,6 +957,8 @@
 
 	case Bnconc:
 	  DISCARD (1);
+	  /* nconc2 GCPROs before calling this. */
+	  /* GCPRO_STACK; */
 	  TOP = bytecode_nconc2 (&TOP);
 	  break;
 
@@ -969,12 +997,16 @@
 	  break;
 
 	case Binsert:
+	  /* Says it can GC. */
+	  /* GCPRO_STACK; */
 	  TOP = Finsert (1, &TOP);
 	  break;
 
 	case BinsertN:
 	  n = READ_UINT_1;
 	  DISCARD (n - 1);
+	  /* See Binsert. */
+	  /* GCPRO_STACK; */
 	  TOP = Finsert (n, &TOP);
 	  break;
 
@@ -1004,6 +1036,8 @@
 	case Bequal:
 	  {
 	    Lisp_Object arg = POP;
+	    /* Can QUIT, so can GC, right? */
+	    /* GCPRO_STACK; */
 	    TOP = Fequal (TOP, arg);
 	    break;
 	  }
@@ -1025,6 +1059,8 @@
 	case Bmember:
 	  {
 	    Lisp_Object arg = POP;
+	    /* Can QUIT, so can GC, right? */
+	    /* GCPRO_STACK; */
 	    TOP = Fmember (TOP, arg);
 	    break;
 	  }
@@ -1059,6 +1095,8 @@
 	case Bskip_chars_forward:
 	  {
 	    Lisp_Object arg = POP;
+	    /* Can QUIT, so can GC, right? */
+	    /* GCPRO_STACK; */
 	    TOP = Fskip_chars_forward (TOP, arg, Qnil);
 	    break;
 	  }
--- a/src/fns.c	Thu Feb 19 22:50:36 2004 +0000
+++ b/src/fns.c	Fri Feb 20 07:29:23 2004 +0000
@@ -1140,6 +1140,7 @@
 */
        (n, list))
 {
+  /* This function can GC */
   REGISTER EMACS_INT i;
   REGISTER Lisp_Object tail = list;
   CHECK_NATNUM (n);
@@ -1164,6 +1165,7 @@
 */
        (n, list))
 {
+  /* This function can GC */
   return Fcar (Fnthcdr (n, list));
 }
 
@@ -1172,6 +1174,7 @@
 */
        (sequence, n))
 {
+  /* This function can GC */
  retry:
   CHECK_INT_COERCE_CHAR (n); /* yuck! */
   if (LISTP (sequence))
@@ -2720,6 +2723,7 @@
 */
        (object, property, value))
 {
+  /* This function cannot GC */
   CHECK_LISP_WRITEABLE (object);
 
   if (LRECORDP (object) && XRECORD_LHEADER_IMPLEMENTATION (object)->putprop)