Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 22 Sep 2011 22:08:09 +0000 (UTC)
From:      Gabor Kovesdan <gabor@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r225733 - stable/8/lib/libc/nls
Message-ID:  <201109222208.p8MM89FK040875@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gabor
Date: Thu Sep 22 22:08:09 2011
New Revision: 225733
URL: http://svn.freebsd.org/changeset/base/225733

Log:
  MFC r202992:
  
  Cache failing and opened catalogs in catopen() and related functions.
  Continuous catopen() calls cause 4 failig stat(2) each, which means a lot
  of overhead.  It is also a good idea to keep the opened catalogs in the memory
  to speed up further catopen() calls to the same catalog since these catalogs
  are not big at all.  In this case, we count references and only free() the
  allocated space when the reference count reaches 0.  The reads and writes to
  the cache are syncronized with an rwlock when these functions are called from
  a threaded program.
  
  MFC r202993, r203174, r203719, r204110:
  
  Small fixes and style nits for the above change.
  
  Approved by:	delphij (mentor)

Modified:
  stable/8/lib/libc/nls/msgcat.c
Directory Properties:
  stable/8/lib/libc/   (props changed)
  stable/8/lib/libc/stdtime/   (props changed)

Modified: stable/8/lib/libc/nls/msgcat.c
==============================================================================
--- stable/8/lib/libc/nls/msgcat.c	Thu Sep 22 18:21:15 2011	(r225732)
+++ stable/8/lib/libc/nls/msgcat.c	Thu Sep 22 22:08:09 2011	(r225733)
@@ -1,5 +1,6 @@
 /***********************************************************
 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
+Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
 
                         All Rights Reserved
 
@@ -39,6 +40,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
+#include <sys/queue.h>
 
 #include <arpa/inet.h>		/* for ntohl() */
 
@@ -47,6 +49,7 @@ __FBSDID("$FreeBSD$");
 #include <limits.h>
 #include <locale.h>
 #include <nl_types.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -57,38 +60,109 @@ __FBSDID("$FreeBSD$");
 
 #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"
 
+#define RLOCK(fail)	{ int ret;						\
+			  if (__isthreaded &&					\
+			      ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) {	\
+				  errno = ret;					\
+				  return (fail);				\
+			  }}
+#define WLOCK(fail)	{ int ret;						\
+			  if (__isthreaded &&					\
+			      ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) {	\
+				  errno = ret;					\
+				  return (fail);				\
+			  }}
+#define UNLOCK		{ if (__isthreaded)					\
+			      _pthread_rwlock_unlock(&rwlock); }
+
 #define	NLERR		((nl_catd) -1)
 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
+#define SAVEFAIL(n, l, e)	{ WLOCK(NLERR);					\
+				  np = malloc(sizeof(struct catentry));		\
+				  if (np != NULL) {				\
+				  	np->name = strdup(n);			\
+					np->path = NULL;			\
+					np->lang = (l == NULL) ? NULL :		\
+					    strdup(l);				\
+					np->caterrno = e;			\
+				  	SLIST_INSERT_HEAD(&cache, np, list);	\
+				  }						\
+				  UNLOCK;					\
+				  errno = e;					\
+				}
+
+static nl_catd load_msgcat(const char *, const char *, const char *);
 
-static nl_catd load_msgcat(const char *);
+static pthread_rwlock_t		 rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+struct catentry {
+	SLIST_ENTRY(catentry)	 list;
+	char			*name;
+	char			*path;
+	int			 caterrno;
+	nl_catd			 catd;
+	char			*lang;
+	int			 refcount;
+};
+
+SLIST_HEAD(listhead, catentry) cache =
+    SLIST_HEAD_INITIALIZER(cache);
 
 nl_catd
 catopen(const char *name, int type)
 {
-	int             spcleft, saverr;
-	char            path[PATH_MAX];
-	char            *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
-	char            *cptr1, *plang, *pter, *pcode;
-	struct stat     sbuf;
+	struct stat sbuf;
+	struct catentry *np;
+	char *base, *cptr, *cptr1, *lang, *nlspath, *pathP, *pcode;
+	char *plang, *pter, *tmpptr;
+	int saverr, spcleft;
+	char path[PATH_MAX];
 
+	/* sanity checking */
 	if (name == NULL || *name == '\0')
 		NLRETERR(EINVAL);
 
-	/* is it absolute path ? if yes, load immediately */
 	if (strchr(name, '/') != NULL)
-		return (load_msgcat(name));
+		/* have a pathname */
+		lang = NULL;
+	else {
+		if (type == NL_CAT_LOCALE)
+			lang = setlocale(LC_MESSAGES, NULL);
+		else
+			lang = getenv("LANG");
+
+		if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
+		    (lang[0] == '.' &&
+		    (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
+		    strchr(lang, '/') != NULL)
+			lang = "C";
+	}
+
+	/* Try to get it from the cache first */
+	RLOCK(NLERR);
+	SLIST_FOREACH(np, &cache, list) {
+		if ((strcmp(np->name, name) == 0) &&
+		    ((lang != NULL && np->lang != NULL &&
+		    strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
+			if (np->caterrno != 0) {
+				/* Found cached failing entry */
+				UNLOCK;
+				NLRETERR(np->caterrno);
+			} else {
+				/* Found cached successful entry */
+				np->refcount++;
+				UNLOCK;
+				return (np->catd);
+			}
+		}
+	}
+	UNLOCK;
 
-	if (type == NL_CAT_LOCALE)
-		lang = setlocale(LC_MESSAGES, NULL);
-	else
-		lang = getenv("LANG");
-
-	if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
-	    (lang[0] == '.' &&
-	     (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
-	    strchr(lang, '/') != NULL)
-		lang = "C";
+	/* is it absolute path ? if yes, load immediately */
+	if (strchr(name, '/') != NULL)
+		return (load_msgcat(name, name, lang));
 
+	/* sanity checking */
 	if ((plang = cptr1 = strdup(lang)) == NULL)
 		return (NLERR);
 	if ((cptr = strchr(cptr1, '@')) != NULL)
@@ -136,7 +210,7 @@ catopen(const char *name, int type)
 						break;
 					case '%':
 						++nlspath;
-						/* fallthrough */
+						/* FALLTHROUGH */
 					default:
 						if (pathP - path >=
 						    sizeof(path) - 1)
@@ -153,6 +227,7 @@ catopen(const char *name, int type)
 			too_long:
 						free(plang);
 						free(base);
+						SAVEFAIL(name, lang, ENAMETOOLONG);
 						NLRETERR(ENAMETOOLONG);
 					}
 					pathP += strlen(tmpptr);
@@ -166,7 +241,7 @@ catopen(const char *name, int type)
 			if (stat(path, &sbuf) == 0) {
 				free(plang);
 				free(base);
-				return (load_msgcat(path));
+				return (load_msgcat(path, name, lang));
 			}
 		} else {
 			tmpptr = (char *)name;
@@ -176,6 +251,7 @@ catopen(const char *name, int type)
 	}
 	free(plang);
 	free(base);
+	SAVEFAIL(name, lang, ENOENT);
 	NLRETERR(ENOENT);
 }
 
@@ -183,19 +259,19 @@ char *
 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
 {
 	struct _nls_cat_hdr *cat_hdr;
-	struct _nls_set_hdr *set_hdr;
 	struct _nls_msg_hdr *msg_hdr;
-	int l, u, i, r;
+	struct _nls_set_hdr *set_hdr;
+	int i, l, r, u;
 
 	if (catd == NULL || catd == NLERR) {
 		errno = EBADF;
 		/* LINTED interface problem */
-		return (char *) s;
-}
+		return ((char *)s);
+	}
 
-	cat_hdr = (struct _nls_cat_hdr *)catd->__data; 
-	set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data
-			+ sizeof(struct _nls_cat_hdr));
+	cat_hdr = (struct _nls_cat_hdr *)catd->__data;
+	set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
+	    sizeof(struct _nls_cat_hdr));
 
 	/* binary search, see knuth algorithm b */
 	l = 0;
@@ -228,7 +304,7 @@ catgets(nl_catd catd, int set_id, int ms
 				} else {
 					l = i + 1;
 				}
-}
+			}
 
 			/* not found */
 			goto notfound;
@@ -238,25 +314,44 @@ catgets(nl_catd catd, int set_id, int ms
 		} else {
 			l = i + 1;
 		}
-}
+	}
 
 notfound:
 	/* not found */
 	errno = ENOMSG;
 	/* LINTED interface problem */
-	return (char *) s;
+	return ((char *)s);
 }
 
 int
 catclose(nl_catd catd)
 {
+	struct catentry *np;
+
+	/* sanity checking */
 	if (catd == NULL || catd == NLERR) {
 		errno = EBADF;
 		return (-1);
 	}
 
-	munmap(catd->__data, (size_t)catd->__size);
-	free(catd);
+	/* Remove from cache if not referenced any more */
+	WLOCK(-1);
+	SLIST_FOREACH(np, &cache, list) {
+		if (catd == np->catd) {
+			np->refcount--;
+			if (np->refcount == 0) {
+				munmap(catd->__data, (size_t)catd->__size);
+				free(catd);
+				SLIST_REMOVE(&cache, np, catentry, list);
+				free(np->name);
+				free(np->path);
+				free(np->lang);
+				free(np);
+			}
+			break;
+		}
+	}
+	UNLOCK;
 	return (0);
 }
 
@@ -265,43 +360,88 @@ catclose(nl_catd catd)
  */
 
 static nl_catd
-load_msgcat(const char *path)
+load_msgcat(const char *path, const char *name, const char *lang)
 {
 	struct stat st;
-	nl_catd catd;
+	nl_catd	catd;
+	struct catentry *np;
 	void *data;
 	int fd;
 
-	/* XXX: path != NULL? */
+	/* path/name will never be NULL here */
 
-	if ((fd = _open(path, O_RDONLY)) == -1)
-		return (NLERR);
+	/*
+	 * One more try in cache; if it was not found by name,
+	 * it might still be found by absolute path.
+	 */
+	RLOCK(NLERR);
+	SLIST_FOREACH(np, &cache, list) {
+		if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
+			np->refcount++;
+			UNLOCK;
+			return (np->catd);
+		}
+	}
+	UNLOCK;
+
+	if ((fd = _open(path, O_RDONLY)) == -1) {
+		SAVEFAIL(name, lang, errno);
+		NLRETERR(errno);
+	}
 
 	if (_fstat(fd, &st) != 0) {
 		_close(fd);
-		return (NLERR);
+		SAVEFAIL(name, lang, EFTYPE);
+		NLRETERR(EFTYPE);
 	}
 
-	data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd,
-	    (off_t)0);
-	_close(fd);
+	/*
+	 * If the file size cannot be held in size_t we cannot mmap()
+	 * it to the memory.  Probably, this will not be a problem given
+	 * that catalog files are usually small.
+	 */
+	if (st.st_size > SIZE_T_MAX) {
+		_close(fd);
+		SAVEFAIL(name, lang, EFBIG);
+		NLRETERR(EFBIG);
+	}
 
-	if (data == MAP_FAILED)
-		return (NLERR);
+	if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
+	    MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
+		int saved_errno = errno;
+		_close(fd);
+		SAVEFAIL(name, lang, saved_errno);
+		NLRETERR(saved_errno);
+	}
+	_close(fd);
 
 	if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
 	    _NLS_MAGIC) {
 		munmap(data, (size_t)st.st_size);
-		NLRETERR(EINVAL);
+		SAVEFAIL(name, lang, EFTYPE);
+		NLRETERR(EFTYPE);
 	}
 
 	if ((catd = malloc(sizeof (*catd))) == NULL) {
 		munmap(data, (size_t)st.st_size);
-		return (NLERR);
+		SAVEFAIL(name, lang, ENOMEM);
+		NLRETERR(ENOMEM);
 	}
 
 	catd->__data = data;
 	catd->__size = (int)st.st_size;
+
+	/* Caching opened catalog */
+	WLOCK(NLERR);
+	if ((np = malloc(sizeof(struct catentry))) != NULL) {
+		np->name = strdup(name);
+		np->path = strdup(path);
+		np->catd = catd;
+		np->lang = (lang == NULL) ? NULL : strdup(lang);
+		np->refcount = 1;
+		np->caterrno = 0;
+		SLIST_INSERT_HEAD(&cache, np, list);
+	}
+	UNLOCK;
 	return (catd);
 }
-



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201109222208.p8MM89FK040875>