Make FcGetDefaultLang and FcGetDefaultLangs thread-safe
authorBehdad Esfahbod <behdad@behdad.org>
Sun, 7 Oct 2012 19:52:25 +0000 (15:52 -0400)
committerBehdad Esfahbod <behdad@behdad.org>
Wed, 2 Jan 2013 06:52:14 +0000 (00:52 -0600)
src/fcdefault.c
src/fcinit.c
src/fcint.h
src/fcstr.c

index d977857..62cf63b 100644 (file)
@@ -25,6 +25,8 @@
 #include "fcint.h"
 #include <string.h>
 
+/* MT-safe */
+
 static const struct {
     FcObject   field;
     FcBool     value;
@@ -32,7 +34,6 @@ static const struct {
     { FC_HINTING_OBJECT,          FcTrue       },  /* !FT_LOAD_NO_HINTING */
     { FC_VERTICAL_LAYOUT_OBJECT,   FcFalse     },  /* FC_LOAD_VERTICAL_LAYOUT */
     { FC_AUTOHINT_OBJECT,         FcFalse      },  /* FC_LOAD_FORCE_AUTOHINT */
-    /* XXX: FC_GLOBAL_ADVANCE is deprecated */
     { FC_GLOBAL_ADVANCE_OBJECT,    FcTrue      },  /* !FC_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH */
     { FC_EMBEDDED_BITMAP_OBJECT,   FcTrue      },  /* !FC_LOAD_NO_BITMAP */
     { FC_DECORATIVE_OBJECT,       FcFalse      },
@@ -40,45 +41,81 @@ static const struct {
 
 #define NUM_FC_BOOL_DEFAULTS   (int) (sizeof FcBoolDefaults / sizeof FcBoolDefaults[0])
 
+FcStrSet *default_langs;
+
 FcStrSet *
 FcGetDefaultLangs (void)
 {
-    FcStrSet *result = FcStrSetCreate ();
-    char *langs;
-
-    langs = getenv ("FC_LANG");
-    if (!langs || !langs[0])
-       langs = getenv ("LC_ALL");
-    if (!langs || !langs[0])
-       langs = getenv ("LC_CTYPE");
-    if (!langs || !langs[0])
-       langs = getenv ("LANG");
-    if (langs && langs[0])
+    FcStrSet *result;
+retry:
+    result = (FcStrSet *) fc_atomic_ptr_get (&default_langs);
+    if (!result)
     {
-       if (!FcStrSetAddLangs (result, langs))
+       char *langs;
+
+       result = FcStrSetCreate ();
+
+       langs = getenv ("FC_LANG");
+       if (!langs || !langs[0])
+           langs = getenv ("LC_ALL");
+       if (!langs || !langs[0])
+           langs = getenv ("LC_CTYPE");
+       if (!langs || !langs[0])
+           langs = getenv ("LANG");
+       if (langs && langs[0])
+       {
+           if (!FcStrSetAddLangs (result, langs))
+               FcStrSetAdd (result, (const FcChar8 *) "en");
+       }
+       else
            FcStrSetAdd (result, (const FcChar8 *) "en");
+
+       FcRefSetConst (&result->ref);
+       if (!fc_atomic_ptr_cmpexch (&default_langs, NULL, result)) {
+           FcRefInit (&result->ref, 1);
+           FcStrSetDestroy (result);
+           goto retry;
+       }
     }
-    else
-       FcStrSetAdd (result, (const FcChar8 *) "en");
 
     return result;
 }
 
+static FcChar8 *default_lang; /* MT-safe */
+
 FcChar8 *
 FcGetDefaultLang (void)
 {
-    static FcChar8 lang_local[128] = {0};
-    FcStrSet *langs;
-
-    if (!lang_local[0])
+    FcChar8 *lang;
+retry:
+    lang = fc_atomic_ptr_get (&default_lang);
+    if (!lang)
     {
-       langs = FcGetDefaultLangs ();
-       strncpy ((char *)lang_local, (const char *)langs->strs[0], 127);
-       lang_local[127] = 0;
+       FcStrSet *langs = FcGetDefaultLangs ();
+       lang = (FcChar8 *) strdup ((const char *) langs->strs[0]);
        FcStrSetDestroy (langs);
+
+       if (!fc_atomic_ptr_cmpexch (&default_lang, NULL, lang)) {
+           free (lang);
+           goto retry;
+       }
     }
 
-    return lang_local;
+    return lang;
+}
+
+void
+FcDefaultFini (void)
+{
+    if (default_lang) {
+       free (default_lang);
+       default_lang = NULL;
+    }
+    if (default_langs) {
+       FcRefInit (&default_langs->ref, 1);
+       FcStrSetDestroy (default_langs);
+       default_langs = NULL;
+    }
 }
 
 void
index 31578ad..75d96a9 100644 (file)
@@ -156,6 +156,7 @@ FcFini (void)
        FcConfigDestroy (_fcConfig);
 
     FcCacheFini ();
+    FcDefaultFini ();
 }
 
 /*
index ab4a7e1..f678b0c 100644 (file)
@@ -761,6 +761,9 @@ FcInitDebug (void);
 FcPrivate FcChar8 *
 FcGetDefaultLang (void);
 
+FcPrivate void
+FcDefaultFini (void);
+
 /* fcdir.c */
 
 FcPrivate FcBool
index e72be5e..f278022 100644 (file)
@@ -1234,6 +1234,10 @@ FcStrSetDestroy (FcStrSet *set)
 {
     int        i;
 
+    /* We rely on this in FcGetDefaultLangs for caching. */
+    if (FcRefIsConst (&set->ref))
+       return;
+
     if (FcRefDec (&set->ref) != 1)
        return;