Print this page
Thread safety fixes.

@@ -41,10 +41,11 @@
 #include <locale.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdio.h>
+#include "mtlib.h"
 #include "collate.h"
 #include "lnumeric.h"   /* for struct lc_numeric */
 #include "lctype.h"     /* for struct lc_ctype */
 #include "setlocale.h"
 #include "../i18n/_loc_path.h"

@@ -57,15 +58,18 @@
 char    *_PathLocale = _DFLT_LOC_PATH;
 
 static char     *current_locale(locale_t, int);
 static void     install_legacy(locale_t, int);
 
+static mutex_t setlocale_lock = DEFAULTMUTEX;
+static locale_t setlocale_list = NULL;
+
 char *
 setlocale(int category, const char *locname)
 {
-        locale_t oldloc;
-        locale_t newloc;
+        locale_t loc;
+        locale_t srch;
         int mask;
 
         if (category < 0 || category > LC_ALL) {
                 errno = EINVAL;
                 return (NULL);

@@ -72,28 +76,50 @@
         }
 
         if (locname == NULL)
                 return (current_locale(___global_locale, category));
 
-        if ((oldloc = duplocale(___global_locale)) == NULL)
-                return (NULL);
-
         mask = (category == LC_ALL ? LC_ALL_MASK : (1 << category));
 
-        newloc = newlocale(mask, locname, oldloc);
-        if (newloc == NULL) {
-                freelocale(oldloc);
+        loc = newlocale(mask, locname, NULL);
+        if (loc == NULL) {
                 return (NULL);
         }
 
-        oldloc = ___global_locale;
-        ___global_locale = newloc;
+        /*
+         * This next logic looks to see if we have ever used the same locale
+         * settings before.  If so, we reuse it.  We avoid ever calling
+         * freelocale() on a locale setting built up by setlocale, this
+         * ensures that consumers (uselocale) will always be thread safe;
+         * the actual locale data objects are never freed, and unique
+         * locale objects are also never freed.  We reuse to avoid leaking
+         * memory in applications that call setlocale repeatedly.
+         */
+        lmutex_lock(&setlocale_lock);
+        for (srch = setlocale_list; srch != NULL; srch = srch->next) {
+                if (memcmp(srch->locdata, loc->locdata,
+                    sizeof (loc->locdata[0]) * LC_ALL) != 0) {
+                        continue;
+                }
+                break;
+        }
 
-        install_legacy(newloc, mask);
+        if (srch == NULL) {
+                /* this is a new locale, save it for reuse later */
+                loc->next = setlocale_list;
+                setlocale_list = loc;
+        } else {
+                /* we already had it, toss the new, and use what we found */
+                freelocale(loc);
+                loc = srch;
+        }
+        ___global_locale = loc;
 
-        freelocale(oldloc);
-        return (current_locale(newloc, category));
+        install_legacy(loc, mask);
+        lmutex_unlock(&setlocale_lock);
+
+        return (current_locale(loc, category));
 }
 
 static char *
 current_locale(locale_t loc, int cat)
 {