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 08:10:31 +0200

Version 2 of API dictionary. Previous one didn't take into account that
some sub-processes might not initialize ns_term while still wanting to
know native support for images which crashed subprocesses.

This one initializes upon first usage (and is retained throughout lifetime).

With all patches combined:
Mem(Init->10h uptime->GC) - Mem(Init) = +- 1MiB

Best,
Przemysław Alexander Kamiński
-- 

Pointers are real. They’re what the hardware understands. Somebody has
to deal with them. You can’t just place a LISP book on top of an x86
chip and hope that the hardware learns about lambda calculus by osmosis.

  -- James Mickens "The Night Watch"

diff --git a/src/nsimage.m b/src/nsimage.m
index 8f1653d085..977694bab7 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -56,9 +56,57 @@
 
    ========================================================================== */
 
+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);
+  image_api_dict = 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();
+  return (bool) (CFDictionaryGetValue(image_api_dict, type) || NO);
+#endif
   NSString *imageType = @"unknown";
   NSArray *types;
 
@@ -67,32 +115,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 +126,6 @@
     imageType = @"tiff";
 
   types = [NSImage imageFileTypes];
-#endif
 
   /* Check if the type is supported on this system.  */
   if ([types indexOfObject:imageType] != NSNotFound)