Browse Source

replace atomics with locks in locale-setting code

this is part of a general program of removing direct use of atomics
where they are not necessary to meet correctness or performance needs,
but in this case it's also an optimization. only the global locale
needs synchronization; allocated locales referenced with locale_t
handles are immutable during their lifetimes, and using atomics to
initialize them increases their cost of setup.
master
Rich Felker 11 years ago
parent
commit
63c188ec42
  1. 35
      src/locale/__setlocalecat.c
  2. 48
      src/locale/setlocale.c

35
src/locale/__setlocalecat.c

@ -17,8 +17,9 @@ char *__strchrnul(const char *, int);
static struct __locale_map *findlocale(const char *name, size_t n) static struct __locale_map *findlocale(const char *name, size_t n)
{ {
static int lock[2];
static void *volatile loc_head; static void *volatile loc_head;
struct __locale_map *p, *new, *old_head; struct __locale_map *p, *new = 0;
const char *path = 0, *z; const char *path = 0, *z;
char buf[256]; char buf[256];
size_t l; size_t l;
@ -28,11 +29,18 @@ static struct __locale_map *findlocale(const char *name, size_t n)
for (p=loc_head; p; p=p->next) for (p=loc_head; p; p=p->next)
if (!strcmp(name, p->name)) return p; if (!strcmp(name, p->name)) return p;
LOCK(lock);
for (p=loc_head; p; p=p->next)
if (!strcmp(name, p->name)) {
UNLOCK(lock);
return p;
}
if (!libc.secure) path = getenv("MUSL_LOCPATH"); if (!libc.secure) path = getenv("MUSL_LOCPATH");
/* FIXME: add a default path? */ /* FIXME: add a default path? */
if (!path) return 0;
for (; *path; path=z+!!*z) { if (path) for (; *path; path=z+!!*z) {
z = __strchrnul(path, ':'); z = __strchrnul(path, ':');
l = z - path - !!*z; l = z - path - !!*z;
if (l >= sizeof buf - n - 2) continue; if (l >= sizeof buf - n - 2) continue;
@ -45,20 +53,19 @@ static struct __locale_map *findlocale(const char *name, size_t n)
new = malloc(sizeof *new); new = malloc(sizeof *new);
if (!new) { if (!new) {
__munmap((void *)map, map_size); __munmap((void *)map, map_size);
return 0; break;
} }
new->map = map; new->map = map;
new->map_size = map_size; new->map_size = map_size;
memcpy(new->name, name, n); memcpy(new->name, name, n);
new->name[n] = 0; new->name[n] = 0;
do { new->next = loc_head;
old_head = loc_head; loc_head = new;
new->next = old_head; break;
} while (a_cas_p(&loc_head, old_head, new) != old_head);
return new;
} }
} }
return 0; UNLOCK(lock);
return new;
} }
static const char envvars[][12] = { static const char envvars[][12] = {
@ -85,11 +92,10 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
int builtin = (val[0]=='C' && !val[1]) int builtin = (val[0]=='C' && !val[1])
|| !strcmp(val, "C.UTF-8") || !strcmp(val, "C.UTF-8")
|| !strcmp(val, "POSIX"); || !strcmp(val, "POSIX");
struct __locale_map *data, *old;
switch (cat) { switch (cat) {
case LC_CTYPE: case LC_CTYPE:
a_store(&loc->ctype_utf8, !builtin || val[1]=='.'); loc->ctype_utf8 = !builtin || val[1]=='.';
break; break;
case LC_MESSAGES: case LC_MESSAGES:
if (builtin) { if (builtin) {
@ -100,10 +106,7 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
} }
/* fall through */ /* fall through */
default: default:
data = builtin ? 0 : findlocale(val, n); loc->cat[cat-2] = builtin ? 0 : findlocale(val, n);
if (data == loc->cat[cat-2]) break;
do old = loc->cat[cat-2];
while (a_cas_p(&loc->cat[cat-2], old, data) != old);
case LC_NUMERIC: case LC_NUMERIC:
break; break;
} }

48
src/locale/setlocale.c

@ -7,8 +7,29 @@
static char buf[2+4*(LOCALE_NAME_MAX+1)]; static char buf[2+4*(LOCALE_NAME_MAX+1)];
static char *setlocale_one_unlocked(int cat, const char *name)
{
struct __locale_map *lm;
if (name) __setlocalecat(&libc.global_locale, cat, name);
switch (cat) {
case LC_CTYPE:
return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
case LC_NUMERIC:
return "C";
case LC_MESSAGES:
return libc.global_locale.messages_name[0]
? libc.global_locale.messages_name : "C";
default:
lm = libc.global_locale.cat[cat-2];
return lm ? lm->name : "C";
}
}
char *setlocale(int cat, const char *name) char *setlocale(int cat, const char *name)
{ {
static volatile int lock[2];
struct __locale_map *lm; struct __locale_map *lm;
int i, j; int i, j;
@ -19,6 +40,8 @@ char *setlocale(int cat, const char *name)
if ((unsigned)cat > LC_ALL) return 0; if ((unsigned)cat > LC_ALL) return 0;
LOCK(lock);
/* For LC_ALL, setlocale is required to return a string which /* For LC_ALL, setlocale is required to return a string which
* encodes the current setting for all categories. The format of * encodes the current setting for all categories. The format of
* this string is unspecified, and only the following code, which * this string is unspecified, and only the following code, which
@ -37,12 +60,13 @@ char *setlocale(int cat, const char *name)
memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX); memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX);
for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--) for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--)
part[j] = 0; part[j] = 0;
setlocale(i, part); setlocale_one_unlocked(i, part);
} }
setlocale(LC_MESSAGES, name + 2 + 3*(LOCALE_NAME_MAX+1)); setlocale_one_unlocked(LC_MESSAGES, name
+ 2 + 3*(LOCALE_NAME_MAX+1));
} else { } else {
for (i=0; i<LC_ALL; i++) for (i=0; i<LC_ALL; i++)
setlocale(i, name); setlocale_one_unlocked(i, name);
} }
} }
memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1)); memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
@ -52,21 +76,13 @@ char *setlocale(int cat, const char *name)
if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1), if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1),
lm->name, strlen(lm->name)); lm->name, strlen(lm->name));
} }
UNLOCK(lock);
return buf; return buf;
} }
if (name) __setlocalecat(&libc.global_locale, cat, name); char *ret = setlocale_one_unlocked(cat, name);
switch (cat) { UNLOCK(lock);
case LC_CTYPE:
return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C"; return ret;
case LC_NUMERIC:
return "C";
case LC_MESSAGES:
return libc.global_locale.messages_name[0]
? libc.global_locale.messages_name : "C";
default:
lm = libc.global_locale.cat[cat-2];
return lm ? lm->name : "C";
}
} }

Loading…
Cancel
Save