libc MT-safety, part 2.

Add a lock to FILE.  flockfile and friends are now implemented
(for the most part) in libc.  flockfile_debug is implemented in
libc_r; I suppose it's about time to kill it but will do it in
a future commit.

Fix a potential deadlock in _fwalk in a threaded environment.
A file flag (__SIGN) was added to stdio.h that, when set, tells
_fwalk to ignore it in its walk.  This seemed to be needed in
refill.c because each file needs to be locked when flushing.

Add a stub for pthread_self in libc.  This is needed by flockfile
which is allowed by POSIX to be recursive.

Make fgetpos() error return value (-1) match man page.

Remove recursive calls to locked functions (stdio); I think I've
got them all, but I may have missed a couple.

A few K&R -> ANSI conversions along with removal of a few instances
of "register".

$Id$ -> $FreeBSD$ in libc/stdio/rget.c

Not objected to:	-arch, a few months ago
This commit is contained in:
Daniel Eischen 2001-02-11 22:06:43 +00:00
parent 5b62961a49
commit 29ac6bd228
22 changed files with 262 additions and 105 deletions

View File

@ -49,8 +49,14 @@
#pragma weak _pthread_mutexattr_destroy=_pthread_mutexattr_destroy_stub
#pragma weak _pthread_mutexattr_settype=_pthread_mutexattr_settype_stub
#pragma weak _pthread_once=_pthread_once_stub
#pragma weak _pthread_self=_pthread_self_stub
#pragma weak _pthread_setspecific=_pthread_setspecific_stub
struct pthread {
};
static struct pthread main_thread;
void *
_pthread_getspecific_stub(pthread_key_t key)
@ -124,6 +130,12 @@ _pthread_once_stub(pthread_once_t *once_control, void (*init_routine) (void))
return (0);
}
pthread_t
_pthread_self_stub(void)
{
return (&main_thread);
}
int
_pthread_setspecific_stub(pthread_key_t key, const void *value)
{

View File

@ -70,6 +70,7 @@
#define pthread_mutexattr_destroy _pthread_mutexattr_destroy
#define pthread_mutexattr_settype _pthread_mutexattr_settype
#define pthread_once _pthread_once
#define pthread_self _pthread_self
#define pthread_setspecific _pthread_setspecific
#define read _read
#define readv _readv
@ -115,7 +116,6 @@
#define pthread_rwlock_wrlock _pthread_rwlock_wrlock
#define pthread_rwlockattr_init _pthread_rwlockattr_init
#define pthread_rwlockattr_destroy _pthread_rwlockattr_destroy
#define pthread_self _pthread_self
#define sched_yield _sched_yield
#define sendfile _sendfile
#define shutdown _shutdown

View File

@ -65,6 +65,7 @@
#undef pthread_mutexattr_destroy
#undef pthread_mutexattr_settype
#undef pthread_once
#undef pthread_self
#undef pthread_setspecific
#undef read
#undef readv
@ -102,7 +103,6 @@
#undef pthread_rwlock_wrlock
#undef pthread_rwlockattr_init
#undef pthread_rwlockattr_destroy
#undef pthread_self
#undef sched_yield
#undef sendfile
#undef shutdown

View File

@ -33,49 +33,155 @@
*
*/
/*
* POSIX stdio FILE locking functions. These assume that the locking
* is only required at FILE structure level, not at file descriptor
* level too.
*
*/
#include "namespace.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "un-namespace.h"
/*
* Declare weak references in case the application is not linked
* with libpthread.
* Weak symbols for externally visible functions in this file:
*/
#pragma weak flockfile=_flockfile_stub
#pragma weak _flockfile=_flockfile_stub
#pragma weak _flockfile_debug=_flockfile_debug_stub
#pragma weak ftrylockfile=_ftrylockfile_stub
#pragma weak _ftrylockfile=_ftrylockfile_stub
#pragma weak funlockfile=_funlockfile_stub
#pragma weak _funlockfile=_funlockfile_stub
#pragma weak flockfile=_flockfile
#pragma weak _flockfile_debug=_flockfile_debug_stub
#pragma weak ftrylockfile=_ftrylockfile
#pragma weak funlockfile=_funlockfile
static int init_lock(FILE *);
/*
* This function is a stub for the _flockfile function in libpthread.
* The FILE lock structure. The FILE *fp is locked when the mutex
* is locked.
*/
void
_flockfile_stub(FILE *fp)
struct __file_lock {
pthread_mutex_t fl_mutex;
pthread_t fl_owner; /* current owner */
int fl_count; /* recursive lock count */
};
/*
* Allocate and initialize a file lock.
*/
static int
init_lock(FILE *fp)
{
struct __file_lock *p;
int ret;
if ((p = malloc(sizeof(struct __file_lock))) == NULL)
ret = -1;
else {
p->fl_mutex = PTHREAD_MUTEX_INITIALIZER;
p->fl_owner = NULL;
p->fl_count = 0;
fp->_lock = p;
ret = 0;
}
return (ret);
}
void
_flockfile(FILE *fp)
{
pthread_t curthread = _pthread_self();
/*
* Check if this is a real file with a valid lock, creating
* the lock if needed:
*/
if ((fp->_file >= 0) &&
((fp->_lock != NULL) || (init_lock(fp) == 0))) {
if (fp->_lock->fl_owner == curthread)
fp->_lock->fl_count++;
else {
/*
* Make sure this mutex is treated as a private
* internal mutex:
*/
_pthread_mutex_lock(&fp->_lock->fl_mutex);
fp->_lock->fl_owner = curthread;
fp->_lock->fl_count = 1;
}
}
}
/*
* This function is a stub for the _flockfile_debug function in libpthread.
* This can be overriden by the threads library if it is linked in.
*/
void
_flockfile_debug_stub(FILE *fp, char *fname, int lineno)
{
_flockfile(fp);
}
/*
* This function is a stub for the _ftrylockfile function in libpthread.
*/
int
_ftrylockfile_stub(FILE *fp)
_ftrylockfile(FILE *fp)
{
return(0);
pthread_t curthread = _pthread_self();
int ret = 0;
/*
* Check if this is a real file with a valid lock, creating
* the lock if needed:
*/
if ((fp->_file >= 0) &&
((fp->_lock != NULL) || (init_lock(fp) == 0))) {
if (fp->_lock->fl_owner == curthread)
fp->_lock->fl_count++;
/*
* Make sure this mutex is treated as a private
* internal mutex:
*/
else if (_pthread_mutex_trylock(&fp->_lock->fl_mutex) == 0) {
fp->_lock->fl_owner = curthread;
fp->_lock->fl_count = 1;
}
else
ret = -1;
}
else
ret = -1;
return (ret);
}
/*
* This function is a stub for the _funlockfile function in libpthread.
*/
void
_funlockfile_stub(FILE *fp)
void
_funlockfile(FILE *fp)
{
pthread_t curthread = _pthread_self();
/*
* Check if this is a real file with a valid lock owned
* by the current thread:
*/
if ((fp->_file >= 0) && (fp->_lock != NULL) &&
(fp->_lock->fl_owner == curthread)) {
/*
* Check if this thread has locked the FILE
* more than once:
*/
if (fp->_lock->fl_count > 1)
/*
* Decrement the count of the number of
* times the running thread has locked this
* file:
*/
fp->_lock->fl_count--;
else {
/*
* The running thread will release the
* lock now:
*/
fp->_lock->fl_count = 0;
fp->_lock->fl_owner = NULL;
_pthread_mutex_unlock(&fp->_lock->fl_mutex);
}
}
}

View File

@ -40,6 +40,8 @@ static char rcsid[] = "$FreeBSD$";
#include <varargs.h>
#endif
#include "local.h"
int
#if __STDC__
asprintf(char **str, char const *fmt, ...)
@ -68,7 +70,7 @@ asprintf(str, fmt, va_alist)
return (-1);
}
f._bf._size = f._w = 127; /* Leave room for the NULL */
ret = vfprintf(&f, fmt, ap);
ret = __vfprintf(&f, fmt, ap); /* Use unlocked __vfprintf */
*f._p = '\0';
va_end(ap);
f._bf._base = reallocf(f._bf._base, f._bf._size + 1);

View File

@ -51,8 +51,7 @@ static const char rcsid[] =
#include "local.h"
int
fclose(fp)
FILE *fp;
fclose(FILE *fp)
{
int r;
@ -70,15 +69,9 @@ fclose(fp)
FREEUB(fp);
if (HASLB(fp))
FREELB(fp);
FUNLOCKFILE(fp);
fp->_file = -1;
fp->_r = fp->_w = 0; /* Mess up if reaccessed. */
#if 0
if (fp->_lock != NULL) {
_pthread_mutex_destroy((pthread_mutex_t *)&fp->_lock);
fp->_lock = NULL;
}
#endif
fp->_flags = 0; /* Release this FILE for reuse. */
FUNLOCKFILE(fp);
return (r);
}

View File

@ -42,16 +42,24 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <stdio.h>
#include "un-namespace.h"
#include "libc_private.h"
/*
* A subroutine version of the macro feof.
* feof has traditionally been a macro in <stdio.h>. That is no
* longer true because it needs to be thread-safe.
*
* #undef feof
*/
#undef feof
int
feof(fp)
FILE *fp;
feof(FILE *fp)
{
return (__sfeof(fp));
int ret;
FLOCKFILE(fp);
ret= __sfeof(fp);
FUNLOCKFILE(fp);
return (ret);
}

View File

@ -42,16 +42,24 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <stdio.h>
#include "un-namespace.h"
#include "libc_private.h"
/*
* A subroutine version of the macro ferror.
* ferror has traditionally been a macro in <stdio.h>. That is no
* longer true because it needs to be thread-safe.
*
* #undef ferror
*/
#undef ferror
int
ferror(fp)
FILE *fp;
ferror(FILE *fp)
{
return (__sferror(fp));
int ret;
FLOCKFILE(fp);
ret = __sferror(fp);
FUNLOCKFILE(fp);
return (ret);
}

View File

@ -49,6 +49,8 @@ static const char rcsid[] =
#include "libc_private.h"
#include "local.h"
static int sflush_locked(FILE *);
/*
* Flush a single file, or (if fp is NULL) all files.
* MT-safe version
@ -59,7 +61,7 @@ fflush(FILE *fp)
int retval;
if (fp == NULL)
return (_fwalk(__sflush));
return (_fwalk(sflush_locked));
FLOCKFILE(fp);
if ((fp->_flags & (__SWR | __SRW)) == 0) {
errno = EBADF;
@ -80,7 +82,7 @@ __fflush(FILE *fp)
int retval;
if (fp == NULL)
return (_fwalk(__sflush));
return (_fwalk(sflush_locked));
if ((fp->_flags & (__SWR | __SRW)) == 0) {
errno = EBADF;
retval = EOF;
@ -120,3 +122,14 @@ __sflush(FILE *fp)
}
return (0);
}
static int
sflush_locked(FILE *fp)
{
int ret;
FLOCKFILE(fp);
ret = __sflush(fp);
FUNLOCKFILE(fp);
return (ret);
}

View File

@ -46,6 +46,7 @@ static const char rcsid[] =
#include <stdio.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "local.h"
int
fgetc(fp)

View File

@ -42,9 +42,12 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "local.h"
/*
@ -54,10 +57,8 @@ static const char rcsid[] =
* so we add 1 here.
#endif
*/
int
__slbexpand(fp, newsize)
FILE *fp;
size_t newsize;
static int
slbexpand(FILE *fp, size_t newsize)
{
void *p;
@ -81,23 +82,23 @@ __slbexpand(fp, newsize)
* it if they wish. Thus, we set __SMOD in case the caller does.
*/
char *
fgetln(fp, lenp)
register FILE *fp;
size_t *lenp;
fgetln(FILE *fp, size_t *lenp)
{
register unsigned char *p;
register size_t len;
unsigned char *p;
size_t len;
size_t off;
FLOCKFILE(fp);
/* make sure there is input */
if (fp->_r <= 0 && __srefill(fp)) {
*lenp = 0;
FUNLOCKFILE(fp);
return (NULL);
}
/* look for a newline in the input */
if ((p = memchr((void *)fp->_p, '\n', (size_t)fp->_r)) != NULL) {
register char *ret;
char *ret;
/*
* Found one. Flag buffer as modified to keep fseek from
@ -110,6 +111,7 @@ fgetln(fp, lenp)
fp->_flags |= __SMOD;
fp->_r -= len;
fp->_p = p;
FUNLOCKFILE(fp);
return (ret);
}
@ -124,14 +126,14 @@ fgetln(fp, lenp)
#define OPTIMISTIC 80
for (len = fp->_r, off = 0;; len += fp->_r) {
register size_t diff;
size_t diff;
/*
* Make sure there is room for more bytes. Copy data from
* file buffer to line buffer, refill file and look for
* newline. The loop stops only when we find a newline.
*/
if (__slbexpand(fp, len + OPTIMISTIC))
if (slbexpand(fp, len + OPTIMISTIC))
goto error;
(void)memcpy((void *)(fp->_lb._base + off), (void *)fp->_p,
len - off);
@ -145,7 +147,7 @@ fgetln(fp, lenp)
p++;
diff = p - fp->_p;
len += diff;
if (__slbexpand(fp, len))
if (slbexpand(fp, len))
goto error;
(void)memcpy((void *)(fp->_lb._base + off), (void *)fp->_p,
diff);
@ -157,9 +159,11 @@ fgetln(fp, lenp)
#ifdef notdef
fp->_lb._base[len] = 0;
#endif
FUNLOCKFILE(fp);
return ((char *)fp->_lb._base);
error:
*lenp = 0; /* ??? */
FUNLOCKFILE(fp);
return (NULL); /* ??? */
}

View File

@ -42,19 +42,16 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <stdio.h>
#include "un-namespace.h"
#include "libc_private.h"
int
fgetpos(fp, pos)
FILE *fp;
fpos_t *pos;
fgetpos(FILE *fp, fpos_t *pos)
{
int retval;
FLOCKFILE(fp);
retval = (*pos = ftello(fp)) == (fpos_t)-1;
FUNLOCKFILE(fp);
return(retval);
/*
* ftello is thread-safe; no need to lock fp.
*/
if ((*pos = ftello(fp)) == (fpos_t)-1)
return (-1);
else
return (0);
}

View File

@ -45,13 +45,14 @@ static const char rcsid[] =
#include <stdio.h>
/*
* A subroutine version of the macro fileno.
* fileno has traditionally been a macro in <stdio.h>. That is
* no longer true because it needs to be thread-safe.
*
* #undef fileno
*/
#undef fileno
int
fileno(fp)
FILE *fp;
fileno(FILE *fp)
{
/* ??? - Should probably use atomic_read. */
return (__sfileno(fp));
}

View File

@ -82,7 +82,7 @@ static spinlock_t thread_lock = _SPINLOCK_INITIALIZER;
#define THREAD_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock)
#if NOT_YET
#define SET_GLUE_PTR(ptr, val) atomic_set_ptr(&(ptr), (uintptr_t)(val))
#define SET_GLUE_PTR(ptr, val) atomic_set_rel_ptr(&(ptr), (uintptr_t)(val))
#else
#define SET_GLUE_PTR(ptr, val) ptr = val
#endif
@ -150,7 +150,7 @@ found:
fp->_ub._size = 0;
fp->_lb._base = NULL; /* no line buffer */
fp->_lb._size = 0;
/* fp->_lock = NULL; */
/* fp->_lock = NULL; */ /* once set always set (reused) */
return (fp);
}
@ -203,7 +203,7 @@ _cleanup()
void
__sinit()
{
/* make sure we clean up on exit */
/* Make sure we clean up on exit. */
__cleanup = _cleanup; /* conservative */
__sdidinit = 1;
}

View File

@ -74,7 +74,6 @@ fopen(file, mode)
fp->_write = __swrite;
fp->_seek = __sseek;
fp->_close = __sclose;
/* fp->_lock = NULL; */
/*
* When opening in append mode, even though we use O_APPEND,
* we need to seek to the end so that ftell() gets the right

View File

@ -62,9 +62,9 @@ static const char rcsid[] =
FILE *
freopen(file, mode, fp)
const char *file, *mode;
register FILE *fp;
FILE *fp;
{
register int f;
int f;
int flags, isopen, oflags, sverrno, wantfd;
if ((flags = __sflags(mode, &oflags)) == 0) {

View File

@ -61,10 +61,13 @@ _fwalk(function)
* It should be safe to walk the list without locking it;
* new nodes are only added to the end and none are ever
* removed.
*
* Avoid locking this list while walking it or else you will
* 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)
if ((fp->_flags != 0) && ((fp->_flags & __SIGN) == 0))
ret |= (*function)(fp);
return (ret);
}

View File

@ -46,11 +46,10 @@ static const char rcsid[] =
#include <stdio.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "local.h"
#undef getc
int
getc(fp)
register FILE *fp;
getc(FILE *fp)
{
int retval;
FLOCKFILE(fp);

View File

@ -42,22 +42,28 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "local.h"
static int lflush __P((FILE *));
static int
lflush(fp)
FILE *fp;
lflush(FILE *fp)
{
int ret = 0;
if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
return (__sflush(fp));
return (0);
if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR)) {
FLOCKFILE(fp);
ret = __sflush(fp);
FUNLOCKFILE(fp);
}
return (ret);
}
/*
@ -65,10 +71,8 @@ lflush(fp)
* Return EOF on eof or error, 0 otherwise.
*/
int
__srefill(fp)
register FILE *fp;
__srefill(FILE *fp)
{
/* make sure stdio is set up */
if (!__sdidinit)
__sinit();
@ -119,8 +123,16 @@ __srefill(fp)
* flush all line buffered output files, per the ANSI C
* standard.
*/
if (fp->_flags & (__SLBF|__SNBF))
if (fp->_flags & (__SLBF|__SNBF)) {
/* Ignore this file in _fwalk to avoid potential deadlock. */
fp->_flags |= __SIGN;
(void) _fwalk(lflush);
fp->_flags &= ~__SIGN;
/* Now flush this file without locking it. */
if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
__sflush(fp);
}
fp->_p = fp->_bf._base;
fp->_r = (*fp->_read)(fp->_cookie, (char *)fp->_p, fp->_bf._size);
fp->_flags &= ~__SMOD; /* buffer contents are again pristine */

View File

@ -39,22 +39,19 @@
static char sccsid[] = "@(#)rget.c 8.1 (Berkeley) 6/4/93";
#endif
static const char rcsid[] =
"$Id";
"$FreeBSD$";
#endif /* LIBC_SCCS and not lint */
#include <stdio.h>
#include "local.h"
int
__srefill(FILE *);
/*
* Handle getc() when the buffer ran out:
* Refill, then return the first character
* in the newly-filled buffer.
*/
int __srget(fp)
register FILE *fp;
int
__srget(FILE *fp)
{
if (__srefill(fp) == 0) {
fp->_r--;

View File

@ -50,6 +50,8 @@ static const char rcsid[] =
#include <varargs.h>
#endif
#include "local.h"
#if __STDC__
int
snprintf(char *str, size_t n, char const *fmt, ...)
@ -81,7 +83,7 @@ snprintf(str, n, fmt, va_alist)
f._flags = __SWR | __SSTR;
f._bf._base = f._p = (unsigned char *)str;
f._bf._size = f._w = n;
ret = vfprintf(&f, fmt, ap);
ret = __vfprintf(&f, fmt, ap);
if (on > 0)
*f._p = '\0';
va_end(ap);

View File

@ -74,7 +74,7 @@ sprintf(str, fmt, va_alist)
#else
va_start(ap);
#endif
ret = vfprintf(&f, fmt, ap);
ret = __vfprintf(&f, fmt, ap);
va_end(ap);
*f._p = 0;
return (ret);