Date: Sat, 13 Nov 2010 13:24:48 +0100 From: Jilles Tjoelker <jilles@stack.nl> To: David Xu <davidxu@freebsd.org> Cc: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: Re: svn commit: r215071 - in user/davidxu/libthr: include lib/libc lib/libc/gen lib/libc/stdio lib/libthr lib/libthr/thread Message-ID: <20101113122447.GA79975@stack.nl> In-Reply-To: <4CDD411D.4060304@freebsd.org> References: <201011100127.oAA1Rmrh069656@svn.freebsd.org> <20101112131941.GA57806@stack.nl> <4CDD411D.4060304@freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
On Fri, Nov 12, 2010 at 09:29:01PM +0800, David Xu wrote:
> Jilles Tjoelker wrote:
> > Apart from supporting process-shared, this also helps avoid the "array
> > of synchronization objects" anti-pattern (false sharing). It is not so
> > bad for the old struct pthread_mutex which is 64 bytes on i386, but in
> > other cases one cache line may contain parts of multiple unrelated
> > synchronization objects.
> Don't know what did you mean, did you think that sizeof struct
> pthread_mutex
> smaller than a cache line is a bad thing ?
It is a bad thing if the rest of the cache line is not used for a
related purpose.
Code like:
lock1 = malloc(sizeof(struct pthread_mutex));
lock2 = malloc(sizeof(struct pthread_mutex));
is likely to place the two locks in the same cache line, which may
defeat much of the advantage of having two locks instead of one.
With the new pthread_mutex_t, applications can do
struct foo {
int protectedfield1;
int protectedfield2;
pthread_mutex_t lock;
};
and the cache line fetch for the lock will bring in the protected fields
as well. Applications are responsible for making the struct large enough
to avoid false sharing.
> > In this regard, it would be better for stdio to allocate
> > struct { FILE file; pthread_mutex_t lock; }
> > rather than separate FILEs and locks.
> This breaks ABI. :-)
Yes, but you can keep the pthread_mutex_t *_fl_mutex and keep accessing
the mutex using that. My concern is only about the way things are laid
out in memory, your approach is otherwise good.
I'm including a lightly tested patch with what I'm thinking of. Of
course, stdio is not a high-performance I/O facility, but it is used
quite a bit and doing it right here may serve as a good example.
> > Like the sem_t change, this changes the ABI in a way symver can only
> > partially deal with (think plugins with pthread_mutex_t in struct in
> > public header file). However, I think the change should be made
> > regardless.
> Yes, we found that some GNU libraries have to bump version.
--
Jilles Tjoelker
[-- Attachment #2 --]
Index: user/davidxu/libthr/lib/libc/stdio/fwalk.c
===================================================================
--- user/davidxu/libthr/lib/libc/stdio/fwalk.c (revision 215181)
+++ user/davidxu/libthr/lib/libc/stdio/fwalk.c (working copy)
@@ -46,7 +46,7 @@
_fwalk(function)
int (*function)(FILE *);
{
- FILE *fp;
+ struct fullfile *ff;
int n, ret;
struct glue *g;
@@ -60,8 +60,9 @@
* introduce a potential deadlock in [at least] refill.c.
*/
for (g = &__sglue; g != NULL; g = g->next)
- for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
- if ((fp->_flags != 0) && ((fp->_flags & __SIGN) == 0))
- ret |= (*function)(fp);
+ for (ff = g->iobs, n = g->niobs; --n >= 0; ff++)
+ if ((ff->file._flags != 0) &&
+ ((ff->file._flags & __SIGN) == 0))
+ ret |= (*function)(&ff->file);
return (ret);
}
Index: user/davidxu/libthr/lib/libc/stdio/glue.h
===================================================================
--- user/davidxu/libthr/lib/libc/stdio/glue.h (revision 215181)
+++ user/davidxu/libthr/lib/libc/stdio/glue.h (working copy)
@@ -33,6 +33,11 @@
* $FreeBSD$
*/
+struct fullfile {
+ FILE file;
+ pthread_mutex_t lock;
+};
+
/*
* The first few FILEs are statically allocated; others are dynamically
* allocated and linked in via this glue structure.
@@ -40,6 +45,6 @@
struct glue {
struct glue *next;
int niobs;
- FILE *iobs;
+ struct fullfile *iobs;
};
extern struct glue __sglue;
Index: user/davidxu/libthr/lib/libc/stdio/findfp.c
===================================================================
--- user/davidxu/libthr/lib/libc/stdio/findfp.c (revision 215181)
+++ user/davidxu/libthr/lib/libc/stdio/findfp.c (working copy)
@@ -55,8 +55,7 @@
#define NDYNAMIC 10 /* add ten more whenever necessary */
-
-#define std(flags, file, lock) { \
+#define std(flags, file, lock) { { \
._flags = (flags), \
._file = (file), \
._cookie = __sF + (file), \
@@ -65,25 +64,20 @@
._seek = __sseek, \
._write = __swrite, \
._fl_mutex = &lock, \
-}
+}, PTHREAD_MUTEX_INITIALIZER }
/* the usual - (stdin + stdout + stderr) */
-static FILE usual[FOPEN_MAX - 3];
+static struct fullfile usual[FOPEN_MAX - 3];
static struct glue uglue = { NULL, FOPEN_MAX - 3, usual };
-static pthread_mutex_t sfLOCK[3] = {
- PTHREAD_MUTEX_INITIALIZER,
- PTHREAD_MUTEX_INITIALIZER,
- PTHREAD_MUTEX_INITIALIZER
-};
-static FILE __sF[3] = {
- std(__SRD, STDIN_FILENO, sfLOCK[0]),
- std(__SWR, STDOUT_FILENO, sfLOCK[1]),
- std(__SWR|__SNBF, STDERR_FILENO, sfLOCK[2])
+static struct fullfile __sF[3] = {
+ std(__SRD, STDIN_FILENO, __sF[0].lock),
+ std(__SWR, STDOUT_FILENO, __sF[1].lock),
+ std(__SWR|__SNBF, STDERR_FILENO, __sF[2].lock)
};
-FILE *__stdinp = &__sF[0];
-FILE *__stdoutp = &__sF[1];
-FILE *__stderrp = &__sF[2];
+FILE *__stdinp = &__sF[0].file;
+FILE *__stdoutp = &__sF[1].file;
+FILE *__stderrp = &__sF[2].file;
struct glue __sglue = { &uglue, 3, __sF };
static struct glue *lastglue = &uglue;
@@ -105,18 +99,23 @@
int n;
{
struct glue *g;
- static FILE empty = { ._fl_mutex = NULL };
- FILE *p;
+ static struct fullfile empty = { .file = { ._fl_mutex = NULL } };
+ struct fullfile *p;
- g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE));
+ g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES +
+ n * sizeof(struct fullfile));
if (g == NULL)
return (NULL);
- p = (FILE *)ALIGN(g + 1);
+ p = (struct fullfile *)ALIGN(g + 1);
g->next = NULL;
g->niobs = n;
g->iobs = p;
- while (--n >= 0)
- *p++ = empty;
+ while (--n >= 0) {
+ *p = empty;
+ p->file._fl_mutex = &p->lock;
+ _pthread_mutex_init(&p->lock, NULL);
+ p++;
+ }
return (g);
}
@@ -127,6 +126,7 @@
__sfp()
{
FILE *fp;
+ struct fullfile *ff;
int n;
struct glue *g;
@@ -137,9 +137,11 @@
*/
THREAD_LOCK();
for (g = &__sglue; g != NULL; g = g->next) {
- for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
- if (fp->_flags == 0)
+ for (ff = g->iobs, n = g->niobs; --n >= 0; ff++)
+ if (ff->file._flags == 0) {
+ fp = &ff->file;
goto found;
+ }
}
THREAD_UNLOCK(); /* don't hold lock while malloc()ing. */
if ((g = moreglue(NDYNAMIC)) == NULL)
@@ -147,7 +149,7 @@
THREAD_LOCK(); /* reacquire the lock */
SET_GLUE_PTR(lastglue->next, g); /* atomically append glue to list */
lastglue = g; /* not atomic; only accessed when locked */
- fp = g->iobs;
+ fp = &g->iobs->file;
found:
fp->_flags = 1; /* reserve this slot; caller sets real flags */
THREAD_UNLOCK();
@@ -163,10 +165,6 @@
fp->_ub._size = 0;
fp->_lb._base = NULL; /* no line buffer */
fp->_lb._size = 0;
- if (fp->_fl_mutex == NULL) { /* once set always set (reused) */
- fp->_fl_mutex = malloc(sizeof(struct pthread_mutex));
- _pthread_mutex_init(fp->_fl_mutex, NULL);
- }
fp->_orientation = 0;
memset(&fp->_mbstate, 0, sizeof(mbstate_t));
return (fp);
Index: user/davidxu/libthr/lib/libc/gen/opendir.c
===================================================================
--- user/davidxu/libthr/lib/libc/gen/opendir.c (revision 215181)
+++ user/davidxu/libthr/lib/libc/gen/opendir.c (working copy)
@@ -112,6 +112,11 @@
int saved_errno;
int unionstack;
struct stat statb;
+ struct {
+ DIR dir;
+ pthread_mutex_t lock;
+ struct _telldir td;
+ } *mem;
dirp = NULL;
/* _fstat() the open handler because the file may have changed. */
@@ -122,10 +127,11 @@
goto fail;
}
if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
- (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
+ (mem = malloc(sizeof(*mem))) == NULL)
goto fail;
- dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
+ dirp = &mem->dir;
+ dirp->dd_td = &mem->td;
LIST_INIT(&dirp->dd_td->td_locq);
dirp->dd_td->td_loccnt = 0;
@@ -298,7 +304,7 @@
dirp->dd_loc = 0;
dirp->dd_fd = fd;
dirp->dd_flags = flags;
- dirp->dd_lock = malloc(sizeof(struct pthread_mutex));
+ dirp->dd_lock = &mem->lock;
_pthread_mutex_init(dirp->dd_lock, NULL);
/*
Index: user/davidxu/libthr/lib/libc/gen/closedir.c
===================================================================
--- user/davidxu/libthr/lib/libc/gen/closedir.c (revision 215181)
+++ user/davidxu/libthr/lib/libc/gen/closedir.c (working copy)
@@ -64,7 +64,6 @@
if (__isthreaded) {
_pthread_mutex_unlock(dirp->dd_lock);
_pthread_mutex_destroy(dirp->dd_lock);
- free(dirp->dd_lock);
}
free((void *)dirp);
return(_close(fd));
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20101113122447.GA79975>
