Subject: bug#3208: 23.0.93; Memory full / crash when displaying lots of characters from a large font (like Arial Unicode or Code2000) which is not explicitly selected (on Win32)

From: Kenichi Handa <[email-masked]>
Date: Fri, 26 Jun 2009 10:26:22 +0900

In article <[email-masked]>, Jason Rumney <[email-masked]> writes:

> It seems to avoid the problem in this case at least. I can now insert 
> over a million of the characters from the original recipe without a 
> problem (and within 5 seconds) by changing the 16 in the original recipe 
> to 20.

> I think this change should go in the release branch,

I think we need the previous change too for 23.1.  Yidon and
Stefan, may I install the attached change in EMACS_23_1_RC?
It seems that this is an effective workaround of the serious
problem on Windows.  Theoretically, the same problem happens
on GNU/Linux too.

> then we can close the bug (or lower its priority if you
> would rather continue to investigate).

Please just lower the priority.  I'll continue to
investigate.  I now have one hypothesis.

On Windows, both uniscribe and gdi font backends returns
many fonts.  When Emacs try to find a suitable font from
them, they are concatinated to a single vector and sorted.
This vector is created and abondoned many times.

If no GC happens while displaying all characters of that
long line, those temporarily created Lisp vectors accumulate
in memory without reused, which may lead to memory full.

What do you think?

---
Kenichi Handa
[email-masked]

2009-06-25  Kenichi Handa  <[email-masked]>

	* fontset.c (fontset_find_font): When a usable rfont_def is found
	in a fallback font-group, make it the first element of the group.
	(fontset_get_font_group): Return 0 if no font-group is set for C.
	(fontset_font): Record the availability of a font for C both in
	the realized fontsets of the current one and the default one.

Index: fontset.c
===================================================================
RCS file: /cvsroot/emacs/emacs/src/fontset.c,v
retrieving revision 1.173
diff -u -r1.173 fontset.c
--- fontset.c	8 Jun 2009 04:33:40 -0000	1.173
+++ fontset.c	26 Jun 2009 01:00:46 -0000
@@ -459,6 +459,12 @@
   XSETCAR (font_group, make_number (charset_ordered_list_tick));
 }
 
+/* Return a font-group (actually a cons (-1 . FONT-GROUP-VECTOR)) for
+   character C in FONTSET.  If C is -1, return a fallback font-group.
+   If C is not -1, the value may be Qt (FONTSET doesn't have a font
+   for C even in the fallback group, or 0 (a font for C may be found
+   only in the fallback group).  */
+
 static Lisp_Object
 fontset_get_font_group (Lisp_Object fontset, int c)
 {
@@ -480,9 +486,10 @@
     font_group = FONTSET_FALLBACK (base_fontset);
   if (NILP (font_group))
     {
+      font_group = make_number (0);
       if (c >= 0)
-	char_table_set_range (fontset, from, to, make_number (0));
-      return Qnil;
+	char_table_set_range (fontset, from, to, font_group);
+      return font_group;
     }
   font_group = Fcopy_sequence (font_group);
   for (i = 0; i < ASIZE (font_group); i++)
@@ -524,9 +531,10 @@
      int id, fallback;
 {
   Lisp_Object vec, font_group;
-  int i, charset_matched = -1;
+  int i, charset_matched = -1, found_index;
   FRAME_PTR f = (FRAMEP (FONTSET_FRAME (fontset)))
     ? XFRAME (selected_frame) : XFRAME (FONTSET_FRAME (fontset));
+  Lisp_Object rfont_def;
 
   font_group = fontset_get_font_group (fontset, fallback ? -1 : c);
   if (! CONSP (font_group))
@@ -546,9 +554,9 @@
 	   first.  */
 	for (i = 0; i < ASIZE (vec); i++)
 	  {
-	    Lisp_Object rfont_def = AREF (vec, i);
 	    Lisp_Object repertory;
 
+	    rfont_def = AREF (vec, i);
 	    if (NILP (rfont_def))
 	      break;
 	    repertory = FONT_DEF_REPERTORY (RFONT_DEF_FONT_DEF (rfont_def));
@@ -564,18 +572,22 @@
   /* Find the first available font in the vector of RFONT-DEF.  */
   for (i = 0; i < ASIZE (vec); i++)
     {
-      Lisp_Object rfont_def, font_def;
+      Lisp_Object font_def;
       Lisp_Object font_entity, font_object;
 
       if (i == 0 && charset_matched >= 0)
 	{
 	  /* Try the element matching with the charset ID at first.  */
 	  rfont_def = AREF (vec, charset_matched);
+	  found_index = charset_matched;
 	  charset_matched = -1;
 	  i--;
 	}
       else if (i != charset_matched)
-	rfont_def = AREF (vec, i);
+	{
+	  rfont_def = AREF (vec, i);
+	  found_index = i;
+	}
       else
 	continue;
 
@@ -623,7 +635,7 @@
 	}
 
       if (font_has_char (f, font_object, c))
-	return rfont_def;
+	goto found;
 
       /* Find a font already opened, maching with the current spec,
 	 and supporting C. */
@@ -637,7 +649,7 @@
 	    break;
 	  font_object = RFONT_DEF_OBJECT (AREF (vec, i));
 	  if (! NILP (font_object) && font_has_char (f, font_object, c))
-	    return rfont_def;
+	    goto found;
 	}
 
       /* Find a font-entity with the current spec and supporting C.  */
@@ -654,6 +666,7 @@
 					     Qnil);
 	  if (NILP (font_object))
 	    continue;
+	  found_index = i;
 	  RFONT_DEF_NEW (rfont_def, font_def);
 	  RFONT_DEF_SET_OBJECT (rfont_def, font_object);
 	  RFONT_DEF_SET_SCORE (rfont_def, RFONT_DEF_SCORE (rfont_def));
@@ -661,10 +674,12 @@
 	  for (j = 0; j < i; j++)
 	    ASET (new_vec, j, AREF (vec, j));
 	  ASET (new_vec, j, rfont_def);
+	  found_index = j;
 	  for (j++; j < ASIZE (new_vec); j++)
 	    ASET (new_vec, j, AREF (vec, j - 1));
 	  XSETCDR (font_group, new_vec);
-	  return rfont_def;
+	  vec = new_vec;
+	  goto found;
 	}
 
       /* No font of the current spec for C.  Try the next spec.  */
@@ -673,6 +688,19 @@
 
   FONTSET_SET (fontset, make_number (c), make_number (0));
   return Qnil;
+
+ found:
+  if (fallback && found_index > 0)
+    {
+      /* The order of fonts in the fallback font-group is not that
+	 important, and it is better to move the found font to the
+	 first of the group so that the next try will find it
+	 quickly. */
+      for (i = found_index; i > 0; i--)
+	ASET (vec, i, AREF (vec, i - 1));
+      ASET (vec, 0, rfont_def);
+    }
+  return rfont_def;
 }

@@ -683,15 +711,15 @@
      struct face *face;
      int id;
 {
-  Lisp_Object rfont_def;
+  Lisp_Object rfont_def, default_rfont_def;
   Lisp_Object base_fontset;
 
   /* Try a font-group of FONTSET. */
   rfont_def = fontset_find_font (fontset, c, face, id, 0);
   if (VECTORP (rfont_def))
     return rfont_def;
-  if (EQ (rfont_def, Qt))
-    goto no_font;
+  if (NILP (rfont_def))
+    FONTSET_SET (fontset, make_number (c), make_number (0));
 
   /* Try a font-group of the default fontset. */
   base_fontset = FONTSET_BASE (fontset);
@@ -700,32 +728,36 @@
       if (NILP (FONTSET_DEFAULT (fontset)))
 	FONTSET_DEFAULT (fontset)
 	  = make_fontset (FONTSET_FRAME (fontset), Qnil, Vdefault_fontset);
-      rfont_def = fontset_find_font (FONTSET_DEFAULT (fontset), c, face, id, 0);
-      if (VECTORP (rfont_def))
-	return rfont_def;
-      if (EQ (rfont_def, Qt))
-	goto no_font;
+      default_rfont_def
+	= fontset_find_font (FONTSET_DEFAULT (fontset), c, face, id, 0);
+      if (VECTORP (default_rfont_def))
+	return default_rfont_def;
+      if (NILP (default_rfont_def))
+	FONTSET_SET (FONTSET_DEFAULT (fontset), make_number (c),
+		     make_number (0));
     }
 
   /* Try a fallback font-group of FONTSET. */
-  rfont_def = fontset_find_font (fontset, c, face, id, 1);
-  if (VECTORP (rfont_def))
-    return rfont_def;
-  if (EQ (rfont_def, Qt))
-    goto no_font;
+  if (! EQ (rfont_def, Qt))
+    {
+      rfont_def = fontset_find_font (fontset, c, face, id, 1);
+      if (VECTORP (rfont_def))
+	return rfont_def;
+      /* Remember that FONTSET has no font for C.  */
+      FONTSET_SET (fontset, make_number (c), Qt);
+    }
 
-  /* Try a fallback font-group of the default fontset . */
-  if (! EQ (base_fontset, Vdefault_fontset))
+  /* Try a fallback font-group of the default fontset. */
+  if (! EQ (base_fontset, Vdefault_fontset)
+      && ! EQ (default_rfont_def, Qt))
     {
       rfont_def = fontset_find_font (FONTSET_DEFAULT (fontset), c, face, id, 1);
       if (VECTORP (rfont_def))
 	return rfont_def;
+      /* Remember that the default fontset has no font for C.  */
+      FONTSET_SET (FONTSET_DEFAULT (fontset), make_number (c), Qt);
     }
 
- no_font:
-  /* Remember that we have no font for C.  */
-  FONTSET_SET (fontset, make_number (c), Qt);
-
   return Qnil;
 }