GNU bug report logs - #25606
[DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.

Previous Next

Package: emacs;

Reported by: Paul Eggert <eggert <at> cs.ucla.edu>

Date: Wed, 1 Feb 2017 23:57:02 UTC

Severity: normal

Tags: patch

Done: npostavs <at> users.sourceforge.net

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 25606 in the body.
You can then email your comments to 25606 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Wed, 01 Feb 2017 23:57:03 GMT) Full text and rfc822 format available.

Acknowledgement sent to Paul Eggert <eggert <at> cs.ucla.edu>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Wed, 01 Feb 2017 23:57:03 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: bug-gnu-emacs <at> gnu.org
Cc: Paul Eggert <eggert <at> cs.ucla.edu>
Subject: [DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.
Date: Wed,  1 Feb 2017 15:56:22 -0800
Use macros like FOR_EACH_TAIL instead of maybe_quit to
catch list cycles automatically instead of relying on the
user becoming impatient and typing C-g.
* src/fns.c (Flength, Fmember, Fmemq, Fmemql, Fassq, Fassoc, Frassq)
(Frassoc, Fdelete, Freverse):
Use FOR_EACH_TAIL instead of maybe_quit.
(Fnreverse): Use simple EQ to check for circular list instead
of rarely_quit, as this suffices in this unusual case.
(Fplist_put, Flax_plist_put, Flax_plist_put):
Use FOR_EACH_TAIL_CONS instead of maybe_quit.
(internal_equal): Use FOR_EACH_TAIL_CONS to check lists, instead
of by-hand tail recursion that did not catch cycles.
* src/fns.c (Fsafe_length, Fplist_get):
* src/xdisp.c (display_mode_element):
Use FOR_EACH_TAIL_SAFE instead of by-hand Floyd’s algorithm.
* src/lisp.h (QUIT_COUNT_HEURISTIC): Remove; no longer needed.
(rarely_quit): Simply count toward USHRT_MAX + 1, since the
fancier versions are no longer needed.
(FOR_EACH_TAIL_CONS, FOR_EACH_TAIL_SAFE)
(FOR_EACH_TAIL_INTERNAL): New macros, the last with definiens
mostly taken from FOR_EACH_TAIL.
(FOR_EACH_TAIL): Rewrite in terms of FOR_EACH_TAIL_INTERNAL.
---
 etc/NEWS    |   3 +
 src/fns.c   | 290 +++++++++++++++++++++++-------------------------------------
 src/lisp.h  |  35 +++++---
 src/xdisp.c |  37 +++-----
 4 files changed, 149 insertions(+), 216 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 86a8385..23e5111 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -872,6 +872,9 @@ collection).
 +++
 ** 'car' and 'cdr' compositions 'cXXXr' and 'cXXXXr' are now part of Elisp.
 
+** Low-level list functions like 'length' and 'member' now do a better
+job of signaling list cycles instead of looping indefinitely.
+
 +++
 ** The new functions 'make-nearby-temp-file' and 'temporary-file-directory'
 can be used for creation of temporary files of remote or mounted directories.
diff --git a/src/fns.c b/src/fns.c
index 4de74a5..b5508fb 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -108,23 +108,11 @@ To get the number of bytes, use `string-bytes'.  */)
     XSETFASTINT (val, ASIZE (sequence) & PSEUDOVECTOR_SIZE_MASK);
   else if (CONSP (sequence))
     {
-      EMACS_INT i = 0;
-
-      do
-	{
-	  ++i;
-	  if ((i & (QUIT_COUNT_HEURISTIC - 1)) == 0)
-	    {
-	      if (MOST_POSITIVE_FIXNUM < i)
-		error ("List too long");
-	      maybe_quit ();
-	    }
-	  sequence = XCDR (sequence);
-	}
-      while (CONSP (sequence));
-
-      CHECK_LIST_END (sequence, sequence);
-
+      intptr_t i = 0;
+      FOR_EACH_TAIL (sequence)
+	i++;
+      if (MOST_POSITIVE_FIXNUM < i)
+	error ("List too long");
       val = make_number (i);
     }
   else if (NILP (sequence))
@@ -142,38 +130,10 @@ it returns 0.  If LIST is circular, it returns a finite value
 which is at least the number of distinct elements.  */)
   (Lisp_Object list)
 {
-  Lisp_Object tail, halftail;
-  double hilen = 0;
-  uintmax_t lolen = 1;
-
-  if (! CONSP (list))
-    return make_number (0);
-
-  /* halftail is used to detect circular lists.  */
-  for (tail = halftail = list; ; )
-    {
-      tail = XCDR (tail);
-      if (! CONSP (tail))
-	break;
-      if (EQ (tail, halftail))
-	break;
-      lolen++;
-      if ((lolen & 1) == 0)
-	{
-	  halftail = XCDR (halftail);
-	  if ((lolen & (QUIT_COUNT_HEURISTIC - 1)) == 0)
-	    {
-	      maybe_quit ();
-	      if (lolen == 0)
-		hilen += UINTMAX_MAX + 1.0;
-	    }
-	}
-    }
-
-  /* If the length does not fit into a fixnum, return a float.
-     On all known practical machines this returns an upper bound on
-     the true length.  */
-  return hilen ? make_float (hilen + lolen) : make_fixnum_or_float (lolen);
+  intptr_t len = 0;
+  FOR_EACH_TAIL_SAFE (list)
+    len++;
+  return make_fixnum_or_float (len);
 }
 
 DEFUN ("string-bytes", Fstring_bytes, Sstring_bytes, 1, 1, 0,
@@ -1383,15 +1343,9 @@ DEFUN ("member", Fmember, Smember, 2, 2, 0,
 The value is actually the tail of LIST whose car is ELT.  */)
   (Lisp_Object elt, Lisp_Object list)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
-    {
-      if (! NILP (Fequal (elt, XCAR (tail))))
-	return tail;
-      rarely_quit (++quit_count);
-    }
-  CHECK_LIST_END (tail, list);
+  FOR_EACH_TAIL (list)
+    if (! NILP (Fequal (elt, XCAR (li.tail))))
+      return li.tail;
   return Qnil;
 }
 
@@ -1400,15 +1354,9 @@ DEFUN ("memq", Fmemq, Smemq, 2, 2, 0,
 The value is actually the tail of LIST whose car is ELT.  */)
   (Lisp_Object elt, Lisp_Object list)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
-    {
-      if (EQ (XCAR (tail), elt))
-	return tail;
-      rarely_quit (++quit_count);
-    }
-  CHECK_LIST_END (tail, list);
+  FOR_EACH_TAIL (list)
+    if (EQ (XCAR (li.tail), elt))
+      return li.tail;
   return Qnil;
 }
 
@@ -1420,16 +1368,12 @@ The value is actually the tail of LIST whose car is ELT.  */)
   if (!FLOATP (elt))
     return Fmemq (elt, list);
 
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
+  FOR_EACH_TAIL (list)
     {
-      Lisp_Object tem = XCAR (tail);
+      Lisp_Object tem = XCAR (li.tail);
       if (FLOATP (tem) && internal_equal (elt, tem, 0, 0, Qnil))
-	return tail;
-      rarely_quit (++quit_count);
+	return li.tail;
     }
-  CHECK_LIST_END (tail, list);
   return Qnil;
 }
 
@@ -1439,15 +1383,9 @@ The value is actually the first element of LIST whose car is KEY.
 Elements of LIST that are not conses are ignored.  */)
   (Lisp_Object key, Lisp_Object list)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
-    {
-      if (CONSP (XCAR (tail)) && EQ (XCAR (XCAR (tail)), key))
-	return XCAR (tail);
-      rarely_quit (++quit_count);
-    }
-  CHECK_LIST_END (tail, list);
+  FOR_EACH_TAIL (list)
+    if (CONSP (XCAR (li.tail)) && EQ (XCAR (XCAR (li.tail)), key))
+      return XCAR (li.tail);
   return Qnil;
 }
 
@@ -1468,17 +1406,13 @@ DEFUN ("assoc", Fassoc, Sassoc, 2, 2, 0,
 The value is actually the first element of LIST whose car equals KEY.  */)
   (Lisp_Object key, Lisp_Object list)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
+  FOR_EACH_TAIL (list)
     {
-      Lisp_Object car = XCAR (tail);
+      Lisp_Object car = XCAR (li.tail);
       if (CONSP (car)
 	  && (EQ (XCAR (car), key) || !NILP (Fequal (XCAR (car), key))))
 	return car;
-      rarely_quit (++quit_count);
     }
-  CHECK_LIST_END (tail, list);
   return Qnil;
 }
 
@@ -1503,15 +1437,9 @@ DEFUN ("rassq", Frassq, Srassq, 2, 2, 0,
 The value is actually the first element of LIST whose cdr is KEY.  */)
   (Lisp_Object key, Lisp_Object list)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
-    {
-      if (CONSP (XCAR (tail)) && EQ (XCDR (XCAR (tail)), key))
-	return XCAR (tail);
-      rarely_quit (++quit_count);
-    }
-  CHECK_LIST_END (tail, list);
+  FOR_EACH_TAIL (list)
+    if (CONSP (XCAR (li.tail)) && EQ (XCDR (XCAR (li.tail)), key))
+      return XCAR (li.tail);
   return Qnil;
 }
 
@@ -1520,17 +1448,13 @@ DEFUN ("rassoc", Frassoc, Srassoc, 2, 2, 0,
 The value is actually the first element of LIST whose cdr equals KEY.  */)
   (Lisp_Object key, Lisp_Object list)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
+  FOR_EACH_TAIL (list)
     {
-      Lisp_Object car = XCAR (tail);
+      Lisp_Object car = XCAR (li.tail);
       if (CONSP (car)
 	  && (EQ (XCDR (car), key) || !NILP (Fequal (XCDR (car), key))))
 	return car;
-      rarely_quit (++quit_count);
     }
-  CHECK_LIST_END (tail, list);
   return Qnil;
 }
 
@@ -1668,23 +1592,20 @@ changing the value of a sequence `foo'.  */)
     }
   else
     {
-      unsigned short int quit_count = 0;
-      Lisp_Object tail, prev;
+      Lisp_Object prev = Qnil;
 
-      for (tail = seq, prev = Qnil; CONSP (tail); tail = XCDR (tail))
+      FOR_EACH_TAIL (seq)
 	{
-	  if (!NILP (Fequal (elt, XCAR (tail))))
+	  if (!NILP (Fequal (elt, (XCAR (li.tail)))))
 	    {
 	      if (NILP (prev))
-		seq = XCDR (tail);
+		seq = XCDR (li.tail);
 	      else
-		Fsetcdr (prev, XCDR (tail));
+		Fsetcdr (prev, XCDR (li.tail));
 	    }
 	  else
-	    prev = tail;
-	  rarely_quit (++quit_count);
+	    prev = li.tail;
 	}
-      CHECK_LIST_END (tail, seq);
     }
 
   return seq;
@@ -1702,15 +1623,17 @@ This function may destructively modify SEQ to produce the value.  */)
     return Freverse (seq);
   else if (CONSP (seq))
     {
-      unsigned short int quit_count = 0;
       Lisp_Object prev, tail, next;
 
       for (prev = Qnil, tail = seq; CONSP (tail); tail = next)
 	{
 	  next = XCDR (tail);
+	  /* If SEQ contains a cycle, attempting to reverse it
+	     in-place will inevitably come back to SEQ.  */
+	  if (EQ (next, seq))
+	    circular_list (seq);
 	  Fsetcdr (tail, prev);
 	  prev = tail;
-	  rarely_quit (++quit_count);
 	}
       CHECK_LIST_END (tail, seq);
       seq = prev;
@@ -1753,13 +1676,9 @@ See also the function `nreverse', which is used more often.  */)
     return Qnil;
   else if (CONSP (seq))
     {
-      unsigned short int quit_count = 0;
-      for (new = Qnil; CONSP (seq); seq = XCDR (seq))
-	{
-	  new = Fcons (XCAR (seq), new);
-	  rarely_quit (++quit_count);
-	}
-      CHECK_LIST_END (seq, seq);
+      new = Qnil;
+      FOR_EACH_TAIL (seq)
+	new = Fcons (XCAR (li.tail), new);
     }
   else if (VECTORP (seq))
     {
@@ -2011,18 +1930,14 @@ corresponding to the given PROP, or nil if PROP is not one of the
 properties on the list.  This function never signals an error.  */)
   (Lisp_Object plist, Lisp_Object prop)
 {
-  Lisp_Object tail, halftail;
-
-  /* halftail is used to detect circular lists.  */
-  tail = halftail = plist;
-  while (CONSP (tail) && CONSP (XCDR (tail)))
+  FOR_EACH_TAIL_SAFE (plist)
     {
-      if (EQ (prop, XCAR (tail)))
-	return XCAR (XCDR (tail));
-
-      tail = XCDR (XCDR (tail));
-      halftail = XCDR (halftail);
-      if (EQ (tail, halftail))
+      if (! CONSP (XCDR (li.tail)))
+	break;
+      if (EQ (prop, XCAR (li.tail)))
+	return XCAR (XCDR (li.tail));
+      li.tail = XCDR (li.tail);
+      if (EQ (li.tail, li.tortoise))
 	break;
     }
 
@@ -2048,19 +1963,22 @@ use `(setq x (plist-put x prop val))' to be sure to use the new value.
 The PLIST is modified by side effects.  */)
   (Lisp_Object plist, Lisp_Object prop, Lisp_Object val)
 {
-  unsigned short int quit_count = 0;
   Lisp_Object prev = Qnil;
-  for (Lisp_Object tail = plist; CONSP (tail) && CONSP (XCDR (tail));
-       tail = XCDR (XCDR (tail)))
+  FOR_EACH_TAIL_CONS (plist)
     {
-      if (EQ (prop, XCAR (tail)))
+      if (! CONSP (XCDR (li.tail)))
+	break;
+
+      if (EQ (prop, XCAR (li.tail)))
 	{
-	  Fsetcar (XCDR (tail), val);
+	  Fsetcar (XCDR (li.tail), val);
 	  return plist;
 	}
 
-      prev = tail;
-      rarely_quit (++quit_count);
+      prev = li.tail;
+      li.tail = XCDR (li.tail);
+      if (EQ (li.tail, li.tortoise))
+	circular_list (plist);
     }
   Lisp_Object newcell
     = Fcons (prop, Fcons (val, NILP (prev) ? plist : XCDR (XCDR (prev))));
@@ -2089,20 +2007,16 @@ corresponding to the given PROP, or nil if PROP is not
 one of the properties on the list.  */)
   (Lisp_Object plist, Lisp_Object prop)
 {
-  unsigned short int quit_count = 0;
-  Lisp_Object tail;
-
-  for (tail = plist;
-       CONSP (tail) && CONSP (XCDR (tail));
-       tail = XCDR (XCDR (tail)))
+  FOR_EACH_TAIL_CONS (plist)
     {
-      if (! NILP (Fequal (prop, XCAR (tail))))
-	return XCAR (XCDR (tail));
-      rarely_quit (++quit_count);
+      if (! CONSP (XCDR (li.tail)))
+	break;
+      if (! NILP (Fequal (prop, XCAR (li.tail))))
+	return XCAR (XCDR (li.tail));
+      li.tail = XCDR (li.tail);
+      if (EQ (li.tail, li.tortoise))
+	circular_list (plist);
     }
-
-  CHECK_LIST_END (tail, prop);
-
   return Qnil;
 }
 
@@ -2116,19 +2030,22 @@ use `(setq x (lax-plist-put x prop val))' to be sure to use the new value.
 The PLIST is modified by side effects.  */)
   (Lisp_Object plist, Lisp_Object prop, Lisp_Object val)
 {
-  unsigned short int quit_count = 0;
   Lisp_Object prev = Qnil;
-  for (Lisp_Object tail = plist; CONSP (tail) && CONSP (XCDR (tail));
-       tail = XCDR (XCDR (tail)))
+  FOR_EACH_TAIL_CONS (plist)
     {
-      if (! NILP (Fequal (prop, XCAR (tail))))
+      if (! CONSP (XCDR (li.tail)))
+	break;
+
+      if (! NILP (Fequal (prop, XCAR (li.tail))))
 	{
-	  Fsetcar (XCDR (tail), val);
+	  Fsetcar (XCDR (li.tail), val);
 	  return plist;
 	}
 
-      prev = tail;
-      rarely_quit (++quit_count);
+      prev = li.tail;
+      li.tail = XCDR (li.tail);
+      if (EQ (li.tail, li.tortoise))
+	circular_list (plist);
     }
   Lisp_Object newcell = list2 (prop, val);
   if (NILP (prev))
@@ -2206,9 +2123,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props,
 	}
     }
 
-  unsigned short int quit_count = 0;
  tail_recurse:
-  rarely_quit (++quit_count);
   if (EQ (o1, o2))
     return 1;
   if (XTYPE (o1) != XTYPE (o2))
@@ -2228,12 +2143,24 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props,
       }
 
     case Lisp_Cons:
-      if (!internal_equal (XCAR (o1), XCAR (o2), depth + 1, props, ht))
-	return 0;
-      o1 = XCDR (o1);
-      o2 = XCDR (o2);
-      /* FIXME: This inf-loops in a circular list!  */
-      goto tail_recurse;
+      {
+	Lisp_Object tail1 = o1;
+	FOR_EACH_TAIL_CONS (o1)
+	  {
+	    if (! CONSP (o2))
+	      return false;
+	    if (! internal_equal (XCAR (li.tail), XCAR (o2), depth + 1,
+				  props, ht))
+	      return false;
+	    tail1 = XCDR (li.tail);
+	    o2 = XCDR (o2);
+	    if (EQ (tail1, o2))
+	      return true;
+	  }
+	o1 = tail1;
+	depth++;
+	goto tail_recurse;
+      }
 
     case Lisp_Misc:
       if (XMISCTYPE (o1) != XMISCTYPE (o2))
@@ -2247,6 +2174,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props,
 	    return 0;
 	  o1 = XOVERLAY (o1)->plist;
 	  o2 = XOVERLAY (o2)->plist;
+	  depth++;
 	  goto tail_recurse;
 	}
       if (MARKERP (o1))
@@ -2397,7 +2325,6 @@ Only the last argument is not altered, and need not be a list.
 usage: (nconc &rest LISTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
-  unsigned short int quit_count = 0;
   Lisp_Object val = Qnil;
 
   for (ptrdiff_t argnum = 0; argnum < nargs; argnum++)
@@ -2413,13 +2340,8 @@ usage: (nconc &rest LISTS)  */)
       CHECK_CONS (tem);
 
       Lisp_Object tail;
-      do
-	{
-	  tail = tem;
-	  tem = XCDR (tail);
-	  rarely_quit (++quit_count);
-	}
-      while (CONSP (tem));
+      FOR_EACH_TAIL_CONS (tem)
+	tail = li.tail;
 
       tem = args[argnum + 1];
       Fsetcdr (tail, tem);
@@ -2841,14 +2763,20 @@ property and a property with the value nil.
 The value is actually the tail of PLIST whose car is PROP.  */)
   (Lisp_Object plist, Lisp_Object prop)
 {
-  unsigned short int quit_count = 0;
-  while (CONSP (plist) && !EQ (XCAR (plist), prop))
+  FOR_EACH_TAIL (plist)
     {
-      plist = XCDR (plist);
-      plist = CDR (plist);
-      rarely_quit (++quit_count);
+      if (EQ (XCAR (li.tail), prop))
+	return li.tail;
+      if (!CONSP (XCDR (li.tail)))
+	{
+	  CHECK_LIST_END (XCDR (li.tail), plist);
+	  return Qnil;
+	}
+      li.tail = XCDR (li.tail);
+      if (EQ (li.tail, li.tortoise))
+	circular_list (plist);
     }
-  return plist;
+  return Qnil;
 }
 
 DEFUN ("widget-put", Fwidget_put, Swidget_put, 3, 3, 0,
diff --git a/src/lisp.h b/src/lisp.h
index 2d74d44..275e0fc 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3129,20 +3129,14 @@ extern void maybe_quit (void);
 
 #define QUITP (!NILP (Vquit_flag) && NILP (Vinhibit_quit))
 
-/* Heuristic on how many iterations of a tight loop can be safely done
-   before it's time to do a quit.  This must be a power of 2.  It
-   is nice but not necessary for it to equal USHRT_MAX + 1.  */
-
-enum { QUIT_COUNT_HEURISTIC = 1 << 16 };
-
 /* Process a quit rarely, based on a counter COUNT, for efficiency.
-   "Rarely" means once per QUIT_COUNT_HEURISTIC or per USHRT_MAX + 1
-   times, whichever is smaller (somewhat arbitrary, but often faster).  */
+   "Rarely" means once per USHRT_MAX + 1 times; this is somewhat
+   arbitrary, but efficient.  */
 
 INLINE void
 rarely_quit (unsigned short int count)
 {
-  if (! (count & (QUIT_COUNT_HEURISTIC - 1)))
+  if (! count)
     maybe_quit ();
 }
 
@@ -4599,13 +4593,32 @@ enum
    http://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf  */
 
 #define FOR_EACH_TAIL(list)						\
+  FOR_EACH_TAIL_INTERNAL (list, CHECK_LIST_END (li.tail, list),		\
+			  circular_list (list))
+
+/* Like FOR_EACH_TAIL (LIST), except check only for cycles.  */
+
+#define FOR_EACH_TAIL_CONS(list) \
+  FOR_EACH_TAIL_INTERNAL (list, (void) 0, circular_list (list))
+
+/* Like FOR_EACH_TAIL (LIST), except check for neither dotted lists
+   nor cycles.  */
+
+#define FOR_EACH_TAIL_SAFE(list) \
+  FOR_EACH_TAIL_INTERNAL (list, (void) 0, (void) (li.tail = Qnil))
+
+/* Like FOR_EACH_TAIL (LIST), except evaluate DOTTED or CYCLE,
+   respectively, if a dotted list or cycle is found.  This is an
+   internal macro intended for use only by the above macros.  */
+
+#define FOR_EACH_TAIL_INTERNAL(list, dotted, cycle)			\
   for (struct { Lisp_Object tail, tortoise; intptr_t n, max; } li	\
 	 = { list, list, 2, 2 };					\
-       CONSP (li.tail) || (CHECK_LIST_END (li.tail, list), false);	\
+       CONSP (li.tail) || (dotted, false);				\
        (li.tail = XCDR (li.tail),					\
 	(li.n-- == 0							\
 	 ? (void) (li.n = li.max <<= 1, li.tortoise = li.tail)		\
-	 : EQ (li.tail, li.tortoise) ? circular_list (list) : (void) 0)))
+	 : EQ (li.tail, li.tortoise) ? (cycle) : (void) 0)))
 
 /* Do a `for' loop over alist values.  */
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 33661c8..31c1fe1 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -23055,30 +23055,19 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
 	    goto tail_recurse;
 	  }
 	else if (STRINGP (car) || CONSP (car))
-	  {
-	    Lisp_Object halftail = elt;
-	    int len = 0;
-
-	    while (CONSP (elt)
-		   && (precision <= 0 || n < precision))
-	      {
-		n += display_mode_element (it, depth,
-					   /* Do padding only after the last
-					      element in the list.  */
-					   (! CONSP (XCDR (elt))
-					    ? field_width - n
-					    : 0),
-					   precision - n, XCAR (elt),
-					   props, risky);
-		elt = XCDR (elt);
-		len++;
-		if ((len & 1) == 0)
-		  halftail = XCDR (halftail);
-		/* Check for cycle.  */
-		if (EQ (halftail, elt))
-		  break;
-	      }
-	  }
+	  FOR_EACH_TAIL_SAFE (elt)
+	    {
+	      if (0 < precision && precision <= n)
+		break;
+	      n += display_mode_element (it, depth,
+					 /* Pad after only the last
+					    list element.  */
+					 (! CONSP (XCDR (li.tail))
+					  ? field_width - n
+					  : 0),
+					 precision - n, XCAR (li.tail),
+					 props, risky);
+	    }
       }
       break;
 
-- 
2.9.3





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Thu, 02 Feb 2017 17:29:01 GMT) Full text and rfc822 format available.

Message #8 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: eggert <at> cs.ucla.edu, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in
 ‘length’ etc.
Date: Thu, 02 Feb 2017 19:28:19 +0200
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Wed,  1 Feb 2017 15:56:22 -0800
> Cc: Paul Eggert <eggert <at> cs.ucla.edu>
> 
> Use macros like FOR_EACH_TAIL instead of maybe_quit to
> catch list cycles automatically instead of relying on the
> user becoming impatient and typing C-g.

I don't think this is a good idea.  We don't know when the user will
become impatient, and on busy systems the counter could be very low by
that time.  I think we shouldn't second-guess users this way, and
should always leave them the possibility of interrupting a possibly
prolonged calculation.

Also, could we please have tests for these functions?

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Thu, 02 Feb 2017 23:02:01 GMT) Full text and rfc822 format available.

Message #11 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.
Date: Thu, 2 Feb 2017 15:01:16 -0800
On 02/02/2017 09:28 AM, Eli Zaretskii wrote:

> could we please have tests for these functions?
Good point, I'll look into adding some.

> I think we shouldn't second-guess users this way, and
> should always leave them the possibility of interrupting a possibly
> prolonged calculation.

Sure, but we shouldn't insert maybe_quit calls after every nanosecond's 
worth of computation; that'd be overkill and would slow down Emacs 
unnecessarily. We should insert a maybe_quit call only when Emacs may 
have done enough computation that it would be annoying if the user typed 
C-g and Emacs did not respond for that period of time.

A reasonable rule of thumb is that if an operation can take longer than 
a garbage collection, then we should insert a maybe_quit in its loop 
body. Conversely, if an operation is always significantly faster then 
GC, then we needn't bother inserting maybe_quit, as Emacs cannot quit in 
the middle of GC anyway (and if we ever get around to fixing *that* 
problem then we will likely be able use the fix approach to attack the 
problem everywhere, not just in GC).

This rule of thumb corresponds pretty closely to what Emacs is already 
doing. For example, Emacs does a maybe_quit while doing regex searching, 
as in practice buffers can be quite large and searching them can easily 
take longer than a GC. In contrast, Emacs does not do a maybe_quit when 
FACE_FOR_CHAR searches font-encoding-charset-alist, as in practice that 
list is never so long that the a user would notice any C-g delay (and if 
the list actually did contain billions of elements (!), GC would be slow 
too as it would have to mark the list).

With cycle checking, all the loops containing removed maybe_quits are 
waaaay faster than a GC would be, which is why these loops don't need 
those maybe_quit calls once they start checking for list cycles.






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Fri, 03 Feb 2017 07:57:01 GMT) Full text and rfc822 format available.

Message #14 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in
 ‘length’ etc.
Date: Fri, 03 Feb 2017 09:55:57 +0200
> Cc: 25606 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Thu, 2 Feb 2017 15:01:16 -0800
> 
> On 02/02/2017 09:28 AM, Eli Zaretskii wrote:
> 
> > could we please have tests for these functions?
> Good point, I'll look into adding some.

Thanks.

> > I think we shouldn't second-guess users this way, and
> > should always leave them the possibility of interrupting a possibly
> > prolonged calculation.
> 
> Sure, but we shouldn't insert maybe_quit calls after every nanosecond's 
> worth of computation; that'd be overkill and would slow down Emacs 
> unnecessarily.

No, not that frequently, I agree.  So perhaps rarely_quit is a better
possibility in these places.

But in general, data structures on which these primitives work could
be rather large, and processing them could take a tangible amount of
time.  Moreover, on a memory-starved system or a system under heavy
computational load, the processing could take a long elapsed time even
if the CPU time used by Emacs is very small.  Users get annoyed by
elapsed time, because that's what they perceive.  So making these
uninterruptible except in case of glaring bugs sounds like losing a
valuable fire escape to me.

> We should insert a maybe_quit call only when Emacs may have done
> enough computation that it would be annoying if the user typed C-g
> and Emacs did not respond for that period of time.

Sure.  But do we have a reliable device to measure this quantity?
Once again, the elapsed time tends to annoy users regardless of the
real computational resources used by Emacs.

IOW, the removal is probably justified for 90% of use cases, maybe
even more, but it could be a problem for a small fraction of use
cases.  And since quitting is a kind of zero-level troubleshooting in
Emacs, I think we should be biased towards those rare cases here.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Fri, 03 Feb 2017 20:30:02 GMT) Full text and rfc822 format available.

Message #17 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.
Date: Fri, 3 Feb 2017 12:29:11 -0800
[Message part 1 (text/plain, inline)]
On 02/02/2017 11:55 PM, Eli Zaretskii wrote:

> the removal is probably justified for 90% of use cases, maybe
> even more

It's 100%, as in practice any use case that has trouble because of the 
code removal will have significantly worse trouble on every GC (which is 
also noninterruptible). memq calls maybe_quit because of circular lists, 
not because of lists of length 1 billion, as huge lists don't work well 
with Emacs anyway (due to GC).

I wrote a little benchmark to try this out (attached), which used the 
new memq. On my workstation at UCLA the results were either:

1. The benchmark was so large that Emacs froze my system while building 
the test-case list, i.e., before any of the newly-affected code was 
executed. This was due to page thrashing. Obviously the draft change is 
irrelevant here.

2. The benchmark was so small that it finished instantly, from the 
user's viewpoint. Obviously not a problem.

3. The benchmark was medium sized (in my case, this was a list with 100 
million entries), so that Emacs was painfully slow but still barely 
usable while the test case was being built or while garbage collection 
was being done. In this case, the new memq was the least of the user's 
problems, as the new memq was 4x faster than a garbage collect so the 
C-g was delayed annoyingly for GC anyway. That is, GC makes Emacs so 
painful to use even with length-100-million lists that people won't use 
Emacs that way and we don't need to worry about treating such lists 
specially.

Of course I can't test all possible use cases. But if there's a 
practical use case where the draft patch will cause a problem, I haven't 
seen it yet.

By the way, I have found many cases where Emacs master will loop forever 
and will ignore C-g. If you like, I can privately send you an Elisp 
expression that, if you evaluate it, you cannot C-g out of. This problem 
is independent of the draft patch, and is far more serious than the 
somewhat-theoretical discussion we're having about memq and friends.

[bigmemq.el (text/plain, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sat, 04 Feb 2017 09:06:01 GMT) Full text and rfc822 format available.

Message #20 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in
 ‘length’ etc.
Date: Sat, 04 Feb 2017 11:05:06 +0200
> Cc: 25606 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Fri, 3 Feb 2017 12:29:11 -0800
> 
> On 02/02/2017 11:55 PM, Eli Zaretskii wrote:
> 
> > the removal is probably justified for 90% of use cases, maybe
> > even more
> 
> It's 100%

There is no 100% in real life, so if you want to convince me, you will
never use such arguments.

> as in practice any use case that has trouble because of the 
> code removal will have significantly worse trouble on every GC (which is 
> also noninterruptible). memq calls maybe_quit because of circular lists, 
> not because of lists of length 1 billion, as huge lists don't work well 
> with Emacs anyway (due to GC).

I don't see how arguments related to GC are relevant to the issue at
hand.  I'm interested in only one aspect of this change: how it is a
good idea to lose the ability to interrupt long loops processing large
Lisp data structures.

> 1. The benchmark was so large that Emacs froze my system while building 
> the test-case list, i.e., before any of the newly-affected code was 
> executed. This was due to page thrashing. Obviously the draft change is 
> irrelevant here.

I disagree that a use case with a paging system is irrelevant.

> 3. The benchmark was medium sized (in my case, this was a list with 100 
> million entries), so that Emacs was painfully slow but still barely 
> usable while the test case was being built or while garbage collection 
> was being done. In this case, the new memq was the least of the user's 
> problems, as the new memq was 4x faster than a garbage collect so the 
> C-g was delayed annoyingly for GC anyway. That is, GC makes Emacs so 
> painful to use even with length-100-million lists that people won't use 
> Emacs that way and we don't need to worry about treating such lists 
> specially.

Once again, the issue with GC is not relevant.  And the fact that
removing some code makes loops faster is trivial and also not
relevant.

I'm sorry, but I remain unconvinced that we should remove these
calls.  I'm okay with replacing maybe_quit with rarely_quit, which
should strike some hopefully more optimal middle ground.

> By the way, I have found many cases where Emacs master will loop forever 
> and will ignore C-g. If you like, I can privately send you an Elisp 
> expression that, if you evaluate it, you cannot C-g out of. This problem 
> is independent of the draft patch, and is far more serious than the 
> somewhat-theoretical discussion we're having about memq and friends.

We need to solve those problems, but that doesn't mean we should
introduce new ones elsewhere.  Being able to interrupt long loops in
Emacs is an important feature.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sat, 04 Feb 2017 19:13:02 GMT) Full text and rfc822 format available.

Message #23 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.
Date: Sat, 4 Feb 2017 11:11:48 -0800
Eli Zaretskii wrote:
> I remain unconvinced that we should remove these calls.

I've provided several use cases and test code, all of which suggest that the 
maybe_quit calls are unnecessary. You have given no use cases that suggest 
otherwise. So I'm puzzled as to why you remain convinced that these calls are 
needed. All I'm asking for is a practical use case; it shouldn't be that hard to 
give one, if the calls are really important.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sat, 04 Feb 2017 19:54:02 GMT) Full text and rfc822 format available.

Message #26 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in
 ‘length’ etc.
Date: Sat, 04 Feb 2017 21:52:59 +0200
> Cc: 25606 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sat, 4 Feb 2017 11:11:48 -0800
> 
> Eli Zaretskii wrote:
> > I remain unconvinced that we should remove these calls.
> 
> I've provided several use cases and test code, all of which suggest that the 
> maybe_quit calls are unnecessary. You have given no use cases that suggest 
> otherwise. So I'm puzzled as to why you remain convinced that these calls are 
> needed. All I'm asking for is a practical use case; it shouldn't be that hard to 
> give one, if the calls are really important.

I did provide use cases, you just dismiss them as unimportant.  They
are rare and perhaps even marginal, but they do exist.  And this
feature exists precisely for rare and marginal situations.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sat, 04 Feb 2017 21:46:02 GMT) Full text and rfc822 format available.

Message #29 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.
Date: Sat, 4 Feb 2017 13:45:21 -0800
Eli Zaretskii wrote:

> I did provide use cases, you just dismiss them as unimportant.

You mentioned use cases consisting of "a memory-starved system or a system under 
heavy computational load". My test code attempted to exercise both 
possibilities, under different scenarios. My tests came up empty: there were no 
cases that caused new problems. Perhaps I misunderstood what you were driving 
at, but if so I would like to know what it was. Possibly there is a 
more-efficient change that would satisfy your concerns, once I understand them.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 05 Feb 2017 18:47:01 GMT) Full text and rfc822 format available.

Message #32 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in
 ‘length’ etc.
Date: Sun, 05 Feb 2017 20:45:48 +0200
> Cc: 25606 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sat, 4 Feb 2017 13:45:21 -0800
> 
> Eli Zaretskii wrote:
> 
> > I did provide use cases, you just dismiss them as unimportant.
> 
> You mentioned use cases consisting of "a memory-starved system or a system under 
> heavy computational load". My test code attempted to exercise both 
> possibilities, under different scenarios. My tests came up empty: there were no 
> cases that caused new problems.

That was your interpretation of the results.  It isn't mine: I don't
think that the fact that in your particular testing GC was a bugger
problem than the uninterruptible loop means the ability to interrupt
those loops has no value.

Besides, one particular simulation of the problem is not convincing
enough anyway.

> Perhaps I misunderstood what you were driving at, but if so I would
> like to know what it was.

I don't see what else can I explain in addition to what I already did.

> Possibly there is a more-efficient change that would satisfy your
> concerns, once I understand them.

How about if I turn the table and ask why is it so important to remove
those calls?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 05 Feb 2017 21:18:02 GMT) Full text and rfc822 format available.

Message #35 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: [DRAFT PATCH 2/2] Signal list cycles in ‘length’ etc.
Date: Sun, 5 Feb 2017 13:17:00 -0800
Eli Zaretskii wrote:

> How about if I turn the table and ask why is it so important to remove
> those calls?

Because they make Emacs bigger and slower for no good reason. A vague feeling 
that there might be a need for them in an unrealistic use case is not a good 
reason. However, your mind is made up and we're wasting time discussing this, so 
I give up and will install a patch with the unnecessary code.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 05 Feb 2017 21:32:02 GMT) Full text and rfc822 format available.

Message #38 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: 25606 <at> debbugs.gnu.org, 25605 <at> debbugs.gnu.org
Subject: patches installed for 25605, 25606
Date: Sun, 5 Feb 2017 13:30:54 -0800
I installed patches for Bug#25605 and Bug#25606, along with all suggested 
revisions to the patches, and am closing the bug reports.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Mon, 06 Feb 2017 16:05:02 GMT) Full text and rfc822 format available.

Message #41 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Mon, 06 Feb 2017 18:04:14 +0200
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sun, 5 Feb 2017 13:30:54 -0800
> 
> I installed patches for Bug#25605 and Bug#25606, along with all suggested 
> revisions to the patches, and am closing the bug reports.

Thanks.

Any reasons why the tests are in manual/cyclic-tests.el, and not in
src/fns-tests.el?  The tests don't need to be run interactively, do
they?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Tue, 07 Feb 2017 06:54:02 GMT) Full text and rfc822 format available.

Message #44 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Mon, 6 Feb 2017 22:53:35 -0800
Eli Zaretskii wrote:
> Any reasons why the tests are in manual/cyclic-tests.el, and not in
> src/fns-tests.el?

No particular reason, no. I don't know my way around the test directories well; 
please feel free to move them.

> The tests don't need to be run interactively, do
> they?

Only if they fail (i.e., loop forever).




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Tue, 07 Feb 2017 12:48:02 GMT) Full text and rfc822 format available.

Message #47 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Philipp Stephani <p.stephani2 <at> gmail.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Tue, 07 Feb 2017 12:47:05 +0000
[Message part 1 (text/plain, inline)]
Paul Eggert <eggert <at> cs.ucla.edu> schrieb am Di., 7. Feb. 2017 um 07:54 Uhr:

>
> > The tests don't need to be run interactively, do
> > they?
>
> Only if they fail (i.e., loop forever).
>
>
If they fail, shouldn't the test runner that runs these tests time out
after some time and mark them as failures?
[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Tue, 07 Feb 2017 16:33:02 GMT) Full text and rfc822 format available.

Message #50 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Philipp Stephani <p.stephani2 <at> gmail.com>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Tue, 7 Feb 2017 08:32:27 -0800
On 02/07/2017 04:47 AM, Philipp Stephani wrote:
> If they fail, shouldn't the test runner that runs these tests time out 
> after some time and mark them as failures? 

Not if the test runner is written in elisp. The loops are deep in the C 
code and are immune to elisp timeouts. (I don't know how the test runner 
works.)





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Tue, 07 Feb 2017 21:49:02 GMT) Full text and rfc822 format available.

Message #53 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Philipp Stephani <p.stephani2 <at> gmail.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Tue, 07 Feb 2017 21:47:42 +0000
[Message part 1 (text/plain, inline)]
Paul Eggert <eggert <at> cs.ucla.edu> schrieb am Di., 7. Feb. 2017 um 17:32 Uhr:

> On 02/07/2017 04:47 AM, Philipp Stephani wrote:
> > If they fail, shouldn't the test runner that runs these tests time out
> > after some time and mark them as failures?
>
> Not if the test runner is written in elisp. The loops are deep in the C
> code and are immune to elisp timeouts. (I don't know how the test runner
> works.)
>
>
How about running the Emacs binary wrapped in /usr/bin/timeout?
[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Tue, 07 Feb 2017 22:21:02 GMT) Full text and rfc822 format available.

Message #56 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Philipp Stephani <p.stephani2 <at> gmail.com>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Tue, 7 Feb 2017 14:20:11 -0800
On 02/07/2017 01:47 PM, Philipp Stephani wrote:
> How about running the Emacs binary wrapped in /usr/bin/timeout?

That should work on GNU platforms. 'timeout' is not a standard program, 
though, and we can't rely on its presence on non-GNU platforms.





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Tue, 07 Feb 2017 22:56:02 GMT) Full text and rfc822 format available.

Message #59 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Philipp Stephani <p.stephani2 <at> gmail.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Tue, 07 Feb 2017 22:55:07 +0000
[Message part 1 (text/plain, inline)]
Paul Eggert <eggert <at> cs.ucla.edu> schrieb am Di., 7. Feb. 2017 um 23:20 Uhr:

> On 02/07/2017 01:47 PM, Philipp Stephani wrote:
> > How about running the Emacs binary wrapped in /usr/bin/timeout?
>
> That should work on GNU platforms. 'timeout' is not a standard program,
> though, and we can't rely on its presence on non-GNU platforms.
>
>
This is only for running tests; don't we already require autotools for that
as well? We could also check for it in configure using AC_CHECK_PROGS.
[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Fri, 10 Feb 2017 10:00:03 GMT) Full text and rfc822 format available.

Message #62 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Fri, 10 Feb 2017 11:59:20 +0200
> Cc: 25606 <at> debbugs.gnu.org, 25605 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Mon, 6 Feb 2017 22:53:35 -0800
> 
> Eli Zaretskii wrote:
> > Any reasons why the tests are in manual/cyclic-tests.el, and not in
> > src/fns-tests.el?
> 
> No particular reason, no. I don't know my way around the test directories well; 
> please feel free to move them.

Done.  I took the liberty to do the commit in your name, to keep the
attribution of the code to the original author.

Btw, 3 of the new tests fail for me:

  Test test-cycle-lax-plist-get backtrace:
    (setq value-1366 (apply fn-1364 args-1365))
    (unwind-protect (setq value-1366 (apply fn-1364 args-1365)) (setq fo
    (not (unwind-protect (setq value-1366 (apply fn-1364 args-1365)) (se
    (if (not (unwind-protect (setq value-1366 (apply fn-1364 args-1365))
    (let (form-description-1368) (if (not (unwind-protect (setq value-13
    (let ((value-1366 (quote ert-form-evaluation-aborted-1367))) (let (f
    (let ((fn-1364 (function lax-plist-get)) (args-1365 (list d1 2))) (l
    (let ((c1 (cyc1 1)) (c2 (cyc2 1 2)) (d1 (dot1 1)) (d2 (dot2 1 2))) (
    (lambda nil (let ((c1 (cyc1 1)) (c2 (cyc2 1 2)) (d1 (dot1 1)) (d2 (d
    ert--run-test-internal([cl-struct-ert--test-execution-info [cl-struc
    ert-run-test([cl-struct-ert-test test-cycle-lax-plist-get nil (lambd
    ert-run-or-rerun-test([cl-struct-ert--stats t [[cl-struct-ert-test f
    ert-run-tests(t #[385 "\306☻\307\"\203G \211\211G\310U\203¶ \211@\20
    ert-run-tests-batch(nil)
    ert-run-tests-batch-and-exit(nil)
    eval((ert-run-tests-batch-and-exit nil))
    command-line-1(("-L" ";." "-l" "ert" "-l" "src/fns-tests.el" "--eval
    command-line()
    normal-top-level()
  Test test-cycle-lax-plist-get condition:
      (wrong-type-argument listp
			   (1 1 1 1 1 1 1 1 1 1 . tail))
     FAILED  18/31  test-cycle-lax-plist-get
  Test test-cycle-lax-plist-put backtrace:
    (setq value-1570 (apply fn-1568 args-1569))
    (unwind-protect (setq value-1570 (apply fn-1568 args-1569)) (setq fo
    (if (unwind-protect (setq value-1570 (apply fn-1568 args-1569)) (set
    (let (form-description-1572) (if (unwind-protect (setq value-1570 (a
    (let ((value-1570 (quote ert-form-evaluation-aborted-1571))) (let (f
    (let ((fn-1568 (function lax-plist-put)) (args-1569 (list d1 2 2)))
    (let ((c1 (cyc1 1)) (c2 (cyc2 1 2)) (d1 (dot1 1)) (d2 (dot2 1 2))) (
    (lambda nil (let ((c1 (cyc1 1)) (c2 (cyc2 1 2)) (d1 (dot1 1)) (d2 (d
    ert--run-test-internal([cl-struct-ert--test-execution-info [cl-struc
    ert-run-test([cl-struct-ert-test test-cycle-lax-plist-put nil (lambd
    ert-run-or-rerun-test([cl-struct-ert--stats t [[cl-struct-ert-test f
    ert-run-tests(t #[385 "\306☻\307\"\203G \211\211G\310U\203¶ \211@\20
    ert-run-tests-batch(nil)
    ert-run-tests-batch-and-exit(nil)
    eval((ert-run-tests-batch-and-exit nil))
    command-line-1(("-L" ";." "-l" "ert" "-l" "src/fns-tests.el" "--eval
    command-line()
    normal-top-level()
  Test test-cycle-lax-plist-put condition:
      (wrong-type-argument listp
			   (1 1 1 1 1 1 1 1 1 1 . tail))
     FAILED  19/31  test-cycle-lax-plist-put
     passed  20/31  test-cycle-length
     passed  21/31  test-cycle-member
     passed  22/31  test-cycle-memq
     passed  23/31  test-cycle-memql
     passed  24/31  test-cycle-nconc
     passed  25/31  test-cycle-plist-get
     passed  26/31  test-cycle-plist-member
  Test test-cycle-plist-put backtrace:
    (setq value-1504 (apply fn-1502 args-1503))
    (unwind-protect (setq value-1504 (apply fn-1502 args-1503)) (setq fo
    (if (unwind-protect (setq value-1504 (apply fn-1502 args-1503)) (set
    (let (form-description-1506) (if (unwind-protect (setq value-1504 (a
    (let ((value-1504 (quote ert-form-evaluation-aborted-1505))) (let (f
    (let ((fn-1502 (function plist-put)) (args-1503 (list d1 2 2))) (let
    (let ((c1 (cyc1 1)) (c2 (cyc2 1 2)) (d1 (dot1 1)) (d2 (dot2 1 2))) (
    (lambda nil (let ((c1 (cyc1 1)) (c2 (cyc2 1 2)) (d1 (dot1 1)) (d2 (d
    ert--run-test-internal([cl-struct-ert--test-execution-info [cl-struc
    ert-run-test([cl-struct-ert-test test-cycle-plist-put nil (lambda ni
    ert-run-or-rerun-test([cl-struct-ert--stats t [[cl-struct-ert-test f
    ert-run-tests(t #[385 "\306☻\307\"\203G \211\211G\310U\203¶ \211@\20
    ert-run-tests-batch(nil)
    ert-run-tests-batch-and-exit(nil)
    eval((ert-run-tests-batch-and-exit nil))
    command-line-1(("-L" ";." "-l" "ert" "-l" "src/fns-tests.el" "--eval
    command-line()
    normal-top-level()
  Test test-cycle-plist-put condition:
      (wrong-type-argument listp
			   (1 1 1 1 1 1 1 1 1 1 . tail))
     FAILED  27/31  test-cycle-plist-put
     passed  28/31  test-cycle-rassoc
     passed  29/31  test-cycle-rassq
     passed  30/31  test-cycle-reverse
     passed  31/31  test-cycle-safe-length

  Ran 31 tests, 28 results as expected, 3 unexpected (2017-02-10 11:49:19+0200)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 08:32:02 GMT) Full text and rfc822 format available.

Message #65 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 00:31:02 -0800
Eli Zaretskii wrote:
> I took the liberty to do the commit in your name, to keep the
> attribution of the code to the original author.
>
> Btw, 3 of the new tests fail for me:

Although the new tests fail for me too, they succeed if I revert commit 
65298ff4d5861cbc8d88162d58c18fa972b81acf, i.e., the commit that you installed in 
my name. So there must be something wrong with that commit. It would be easy to 
revert the commit but I assume that it was done for a reason so I'll wait for 
you to look into the problem.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 16:14:02 GMT) Full text and rfc822 format available.

Message #68 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 18:13:03 +0200
> Cc: 25606 <at> debbugs.gnu.org, 25605 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sun, 12 Feb 2017 00:31:02 -0800
> 
> Eli Zaretskii wrote:
> > I took the liberty to do the commit in your name, to keep the
> > attribution of the code to the original author.
> >
> > Btw, 3 of the new tests fail for me:
> 
> Although the new tests fail for me too, they succeed if I revert commit 
> 65298ff4d5861cbc8d88162d58c18fa972b81acf, i.e., the commit that you installed in 
> my name. So there must be something wrong with that commit.

That's strange.  Before pushing, I verified that I get the same
failures with your original test file.  But maybe I didn't invoke the
test as you intended it to be run.  Here's how I invoke it:

  $ cd test && ../src/emacs -batch -l ert -l manual/cycle-tests.el --eval "(ert-run-tests-batch-and-exit nil)"

If that's wrong, what is the right way of running those tests?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 18:56:02 GMT) Full text and rfc822 format available.

Message #71 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 10:55:00 -0800
Eli Zaretskii wrote:
> what is the right way of running those tests?

"make check". That's what's documented, right?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 19:34:02 GMT) Full text and rfc822 format available.

Message #74 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 21:33:48 +0200
> Cc: 25606 <at> debbugs.gnu.org, 25605 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sun, 12 Feb 2017 10:55:00 -0800
> 
> Eli Zaretskii wrote:
> > what is the right way of running those tests?
> 
> "make check". That's what's documented, right?

This runs everything, which is too much.  How do I run this single
test?  (AFAIU, it runs each test exactly as I did.)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 19:42:02 GMT) Full text and rfc822 format available.

Message #77 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 21:41:18 +0200
> Cc: 25606 <at> debbugs.gnu.org, 25605 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sun, 12 Feb 2017 10:55:00 -0800
> 
> Eli Zaretskii wrote:
> > what is the right way of running those tests?
> 
> "make check". That's what's documented, right?

Actually, AFAIU "make check" doesn't run the tests in manual/ at all,
so I'm not sure how you succeeded to run them.  Or maybe I'm missing
something obvious.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 19:42:03 GMT) Full text and rfc822 format available.

Message #80 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Paul Eggert <eggert <at> cs.ucla.edu>, 25605 <at> debbugs.gnu.org,
 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 20:41:16 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

>> "make check". That's what's documented, right?
>
> This runs everything, which is too much.  How do I run this single
> test?  (AFAIU, it runs each test exactly as I did.)

I don't know how to do a single test, but I always run tests from a
single file like so:

larsi <at> mouse:~/src/emacs/trunk/test$ make src/fns-tests

(which hangs for me at present).

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 19:44:02 GMT) Full text and rfc822 format available.

Message #83 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 11:43:24 -0800
Eli Zaretskii wrote:
> This runs everything, which is too much.  How do I run this single
> test?

Sorry, I don't know. I didn't have to know, as "make check" sufficed for me.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 19:50:02 GMT) Full text and rfc822 format available.

Message #86 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Paul Eggert <eggert <at> cs.ucla.edu>, 25605 <at> debbugs.gnu.org,
 25606 <at> debbugs.gnu.org
Subject: Re: bug#25605: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 20:49:01 +0100
Lars Ingebrigtsen <larsi <at> gnus.org> writes:

> larsi <at> mouse:~/src/emacs/trunk/test$ make src/fns-tests
>
> (which hangs for me at present).

(Hm, doesn't hang after rebuilding Emacs.  But some of the tests fail.)

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 20:23:02 GMT) Full text and rfc822 format available.

Message #89 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Lars Ingebrigtsen <larsi <at> gnus.org>
Cc: eggert <at> cs.ucla.edu, 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 22:22:08 +0200
> From: Lars Ingebrigtsen <larsi <at> gnus.org>
> Cc: Paul Eggert <eggert <at> cs.ucla.edu>,  25605 <at> debbugs.gnu.org,  25606 <at> debbugs.gnu.org
> Date: Sun, 12 Feb 2017 20:41:16 +0100
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> >> "make check". That's what's documented, right?
> >
> > This runs everything, which is too much.  How do I run this single
> > test?  (AFAIU, it runs each test exactly as I did.)
> 
> I don't know how to do a single test, but I always run tests from a
> single file like so:
> 
> larsi <at> mouse:~/src/emacs/trunk/test$ make src/fns-tests

That's what I do.  But this doesn't work for files in manual/.

> (which hangs for me at present).

(It doesn't hang for me; it fails because 3 tests fail.)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Sun, 12 Feb 2017 20:58:02 GMT) Full text and rfc822 format available.

Message #92 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Sun, 12 Feb 2017 12:57:13 -0800
Eli Zaretskii wrote:
> Actually, AFAIU "make check" doesn't run the tests in manual/ at all,
> so I'm not sure how you succeeded to run them.

Well, now that I think back on it, I'm not sure either. I did run them, using 
some complicated set of arguments to 'make', but I don't remember what the 
arguments were.

Anyway, I debugged the tests under the new regime and installed a fix.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Mon, 13 Feb 2017 05:38:02 GMT) Full text and rfc822 format available.

Message #95 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Mon, 13 Feb 2017 07:37:27 +0200
> Cc: 25606 <at> debbugs.gnu.org, 25605 <at> debbugs.gnu.org
> From: Paul Eggert <eggert <at> cs.ucla.edu>
> Date: Sun, 12 Feb 2017 12:57:13 -0800
> 
> Anyway, I debugged the tests under the new regime and installed a fix.

Thanks, they all pass for me now.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Mon, 13 Feb 2017 09:12:02 GMT) Full text and rfc822 format available.

Message #98 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Michael Albinus <michael.albinus <at> gmx.de>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Paul Eggert <eggert <at> cs.ucla.edu>, 25605 <at> debbugs.gnu.org,
 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Mon, 13 Feb 2017 10:11:21 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

>> > what is the right way of running those tests?
>> 
>> "make check". That's what's documented, right?
>
> This runs everything, which is too much.  How do I run this single
> test?  (AFAIU, it runs each test exactly as I did.)

# make -C test fns-tests SELECTOR=\'test-cycle-lax-plist-get

See also test/README

Best regards, Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#25606; Package emacs. (Mon, 13 Feb 2017 14:38:02 GMT) Full text and rfc822 format available.

Message #101 received at 25606 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Michael Albinus <michael.albinus <at> gmx.de>
Cc: eggert <at> cs.ucla.edu, 25605 <at> debbugs.gnu.org, 25606 <at> debbugs.gnu.org
Subject: Re: bug#25606: patches installed for 25605, 25606
Date: Mon, 13 Feb 2017 16:37:18 +0200
> From: Michael Albinus <michael.albinus <at> gmx.de>
> Cc: Paul Eggert <eggert <at> cs.ucla.edu>,  25605 <at> debbugs.gnu.org,  25606 <at> debbugs.gnu.org
> Date: Mon, 13 Feb 2017 10:11:21 +0100
> 
> >> "make check". That's what's documented, right?
> >
> > This runs everything, which is too much.  How do I run this single
> > test?  (AFAIU, it runs each test exactly as I did.)
> 
> # make -C test fns-tests SELECTOR=\'test-cycle-lax-plist-get

Yes, I know.  But that doesn't work for tests under manual/, which is
what I was asking about.




bug closed, send any further explanations to 25606 <at> debbugs.gnu.org and Paul Eggert <eggert <at> cs.ucla.edu> Request was from npostavs <at> users.sourceforge.net to control <at> debbugs.gnu.org. (Sat, 18 Feb 2017 19:55:01 GMT) Full text and rfc822 format available.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sun, 19 Mar 2017 11:24:04 GMT) Full text and rfc822 format available.

This bug report was last modified 7 years and 37 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.