From dbbb46207e243d07d2e4c4d35547fc7521c9c62f Mon Sep 17 00:00:00 2001
From: Akira TAGOH <akira@tagoh.org>
Date: Fri, 16 May 2025 19:45:41 +0900
Subject: [PATCH] Drop FcObjectFini() from FcFini() to fix memory leaks

Initialization around FcObject has been integrated into
FcConfig instance. they are freed once all FcConfig instances
has been destroyed.  So we don't need to call FcObjectFini()
from FcFini() anymore.

Changelog: fixed
---
 src/fccfg.c          |  2 ++
 src/fcinit.c         |  1 -
 src/fcint.h          |  2 ++
 src/fcobjs.c         | 23 ++++++++++++++++++++++-
 test/test-bz106632.c |  1 +
 5 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/src/fccfg.c b/src/fccfg.c
index 0756698e..0d521018 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -210,6 +210,7 @@ FcConfigCreate (void)
     config->prefer_app_fonts = FcFalse;
 
     FcRefInit (&config->ref, 1);
+    FcObjectInit();
 
     return config;
 
@@ -356,6 +357,7 @@ FcConfigDestroy (FcConfig *config)
 	if (FcRefDec (&config->ref) != 1)
 	    return;
 
+	FcObjectFini();
 	(void)fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
 
 	FcStrSetDestroy (config->configDirs);
diff --git a/src/fcinit.c b/src/fcinit.c
index 33e1d6e8..9c66515c 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -214,7 +214,6 @@ void
 FcFini (void)
 {
     FcConfigFini();
-    FcObjectFini();
     FcCacheFini();
 }
 
diff --git a/src/fcint.h b/src/fcint.h
index 2dd0bf57..67a16046 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -1413,6 +1413,8 @@ FcPrivate FcChar8 *
 FcStrSerialize (FcSerialize *serialize, const FcChar8 *str);
 
 /* fcobjs.c */
+FcPrivate void
+FcObjectInit (void);
 
 FcPrivate void
 FcObjectFini (void);
diff --git a/src/fcobjs.c b/src/fcobjs.c
index e21d7a3d..c7bcb0cf 100644
--- a/src/fcobjs.c
+++ b/src/fcobjs.c
@@ -43,6 +43,13 @@ struct FcObjectOtherTypeInfo {
     FcObjectType                  object;
     FcObject                      id;
 } *other_types;
+static FcRef obj_ref = { .count = 0 };
+
+void
+FcObjectInit (void)
+{
+    FcRefInc (&obj_ref);
+}
 
 void
 FcObjectFini (void)
@@ -50,11 +57,17 @@ FcObjectFini (void)
     struct FcObjectOtherTypeInfo *ots, *ot;
 
 retry:
+    if (obj_ref.count < 1)
+	fprintf (stderr, "Fontconfig warning: too many caller of FcObjectFini()\n");
+    if (obj_ref.count >= 1 && FcRefDec (&obj_ref) != 1)
+	return;
     ots = fc_atomic_ptr_get (&other_types);
     if (!ots)
 	return;
-    if (!fc_atomic_ptr_cmpexch (&other_types, ots, NULL))
+    if (!fc_atomic_ptr_cmpexch (&other_types, ots, NULL)) {
+	FcRefInc (&obj_ref);
 	goto retry;
+    }
 
     while (ots) {
 	ot = ots->next;
@@ -69,9 +82,17 @@ static FcObjectType *
 _FcObjectLookupOtherTypeByName (const char *str, FcObject *id)
 {
     struct FcObjectOtherTypeInfo *ots, *ot;
+    static FcBool                 warn = FcFalse;
 
 retry:
     ots = fc_atomic_ptr_get (&other_types);
+    if (obj_ref.count < 1) {
+	if (!warn) {
+	    fprintf (stderr, "Fontconfig warning: using without calling FcInit()\n");
+	    warn = FcTrue;
+	}
+	FcObjectInit();
+    }
 
     for (ot = ots; ot; ot = ot->next)
 	if (0 == strcmp (ot->object.object, str))
diff --git a/test/test-bz106632.c b/test/test-bz106632.c
index 3c56cb72..1e18d5c0 100644
--- a/test/test-bz106632.c
+++ b/test/test-bz106632.c
@@ -305,6 +305,7 @@ bail:
 	FcStrFree (fontdir);
     if (cachedir)
 	FcStrFree (cachedir);
+    FcFini();
 
     return ret;
 }
-- 
GitLab

