Subject: bug#79023: 30.1.90; Suspicion of memory leak on internal_redisplay (MacOS)

From: Przemysław Alexander Kamiński<[email-masked]>
Date: Tue, 12 Aug 2025 16:12:31 +0200

- ns_native_api dictionary update, hopefully final
- macfont_list_cache_0001.patch adds a spec-to-list cache - I noticed
  that it still is leaking (probably with packages that are running
  results in a subprocess)

diff --git a/src/macfont.m b/src/macfont.m
index 2a0b9aa255..d33c7992ca 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -2272,7 +2272,7 @@
 }
 
 static Lisp_Object
-macfont_list (struct frame *f, Lisp_Object spec)
+macfont_list_impl (struct frame *f, Lisp_Object spec)
 {
   Lisp_Object val = Qnil, family, extra;
   int i, n;
@@ -2589,8 +2589,33 @@
   unblock_input ();
 
   return val;
+ }
+
+
+static CFMutableDictionaryRef macfont_list_cache;
+
+static Lisp_Object
+macfont_list (struct frame *f, Lisp_Object spec)
+{
+  if (!macfont_list_cache) {
+      macfont_list_cache
+	= CFDictionaryCreateMutable (NULL, 500, NULL, NULL);
+      CFRetain(macfont_list_cache);
+  }
+
+  Lisp_Object result;
+  if (CFDictionaryGetValueIfPresent (macfont_list_cache, spec,
+				     (void *) &result))
+  {
+    return result;
+  }
+
+  result = macfont_list_impl (f, spec);
+  CFDictionaryAddValue (macfont_list_cache, copy_font_spec(spec), result);
+  return result;
 }
 
+
 static Lisp_Object
 macfont_match (struct frame * frame, Lisp_Object spec)
 {
diff --git a/src/nsimage.m b/src/nsimage.m
index 8f1653d085..e4493994d1 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -56,9 +56,63 @@
 
    ========================================================================== */
 
+static CFDictionaryRef image_api_dict;
+
+/*
+  ns_image_init initializes native support image type cache-dict for
+  ns_can_use_native_image_api. Needs to be initialized ad-hoc since it's
+  possible that non-GUI subprocesses will use it.
+
+  Without the cache-dict hundreds of thousands of small, persistent
+  NSString allocations are happening causing leak 40-60KiB leak per usage.
+*/
+
+static void ns_image_init (void)
+{
+  /* NSImage also supports a host of other types such as PDF and BMP,
+       but we don't yet support these in image.c.  */
+  NSArray *imageTypes = [NSImage imageTypes];
+  Lisp_Object keys[] = { Qnative_image, Qjpeg, Qpng, Qgif, Qtiff,
+#ifndef HAVE_RSVG
+			 Qsvg,
+#endif
+#ifndef HAVE_WEBP
+			 Qwebp,
+#endif
+			 Qheic };
+  bool values[] = {
+    YES,
+    [imageTypes containsObject:@"public.jpeg"],
+    [imageTypes containsObject:@"public.png"],
+    [imageTypes containsObject:@"com.compuserve.gif"],
+    [imageTypes containsObject:@"public.tiff"],
+#ifndef HAVE_RSVG
+    [imageTypes containsObject:@"public.svg-image"],
+#endif
+#ifndef HAVE_WEBP
+    [imageTypes containsObject:@"org.webmproject.webp"],
+#endif
+    [imageTypes containsObject:@"public.heic"],
+  };
+  image_api_dict
+    = CFDictionaryCreate (NULL, (void *) keys, (void *) values, 50,
+			  NULL, NULL);
+  CFRetain (image_api_dict);
+}
+
 bool
 ns_can_use_native_image_api (Lisp_Object type)
 {
+#ifdef NS_IMPL_COCOA
+  if (!image_api_dict)
+    ns_image_init ();
+
+  bool found;
+  CFDictionaryGetValueIfPresent (image_api_dict, type,
+				 (void *) &found);
+  return found;
+
+#endif
   NSString *imageType = @"unknown";
   NSArray *types;
 
@@ -67,32 +121,6 @@
   if (EQ (type, Qnative_image))
     return YES;
 
-#ifdef NS_IMPL_COCOA
-  /* Work out the UTI of the image type.  */
-  if (EQ (type, Qjpeg))
-    imageType = @"public.jpeg";
-  else if (EQ (type, Qpng))
-    imageType = @"public.png";
-  else if (EQ (type, Qgif))
-    imageType = @"com.compuserve.gif";
-  else if (EQ (type, Qtiff))
-    imageType = @"public.tiff";
-#ifndef HAVE_RSVG
-  else if (EQ (type, Qsvg))
-    imageType = @"public.svg-image";
-#endif
-#ifndef HAVE_WEBP
-  else if (EQ (type, Qwebp))
-    imageType = @"org.webmproject.webp";
-#endif
-  else if (EQ (type, Qheic))
-    imageType = @"public.heic";
-
-  /* NSImage also supports a host of other types such as PDF and BMP,
-     but we don't yet support these in image.c.  */
-
-  types = [NSImage imageTypes];
-#else
   /* Work out the image type.  */
   if (EQ (type, Qjpeg))
     imageType = @"jpeg";
@@ -104,7 +132,6 @@
     imageType = @"tiff";
 
   types = [NSImage imageFileTypes];
-#endif
 
   /* Check if the type is supported on this system.  */
   if ([types indexOfObject:imageType] != NSNotFound)

Best,
Przemysław Alexander Kamiński