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)
{