blob: d9104a42f2c233f0e5a08739f01731de337e1e4f [file] [log] [blame]
/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
*
* GNU Library General Public License (LGPL) version 2 or later.
*
* Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
*/
#include "_stdio.h"
#ifdef __DO_UNLOCKED
#ifdef __UCLIBC_MJN3_ONLY__
#warning WISHLIST: Add option to test for undefined behavior of fflush.
#endif /* __UCLIBC_MJN3_ONLY__ */
/* Even if the stream is set to user-locking, we still need to lock
* when all (lbf) writing streams are flushed. */
#define __MY_STDIO_THREADLOCK(__stream) \
__UCLIBC_IO_MUTEX_CONDITIONAL_LOCK((__stream)->__lock, \
(_stdio_user_locking != 2))
#define __MY_STDIO_THREADUNLOCK(__stream) \
__UCLIBC_IO_MUTEX_CONDITIONAL_UNLOCK((__stream)->__lock, \
(_stdio_user_locking != 2))
#if defined(__UCLIBC_HAS_THREADS__) && defined(__STDIO_BUFFERS)
void attribute_hidden _stdio_openlist_dec_use(void)
{
__STDIO_THREADLOCK_OPENLIST_DEL;
if ((_stdio_openlist_use_count == 1) && (_stdio_openlist_del_count > 0)) {
FILE *p = NULL;
FILE *n;
FILE *stream;
#ifdef __UCLIBC_MJN3_ONLY__
#warning REMINDER: As an optimization, we could unlock after we move past the head.
#endif
/* Grab the openlist add lock since we might change the head of the list. */
__STDIO_THREADLOCK_OPENLIST_ADD;
for (stream = _stdio_openlist; stream; stream = n) {
n = stream->__nextopen;
#ifdef __UCLIBC_MJN3_ONLY__
#warning REMINDER: fix for nonatomic
#endif
if ((stream->__modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY|__FLAG_FAILED_FREOPEN))
== (__FLAG_READONLY|__FLAG_WRITEONLY)
) { /* The file was closed and should be removed from the list. */
if (!p) {
_stdio_openlist = n;
} else {
p->__nextopen = n;
}
__STDIO_STREAM_FREE_FILE(stream);
} else {
p = stream;
}
}
__STDIO_THREADUNLOCK_OPENLIST_ADD;
_stdio_openlist_del_count = 0; /* Should be clean now. */
}
--_stdio_openlist_use_count;
__STDIO_THREADUNLOCK_OPENLIST_DEL;
}
#endif
int fflush_unlocked(register FILE *stream)
{
#ifdef __STDIO_BUFFERS
int retval = 0;
#ifdef __UCLIBC_MJN3_ONLY__
#warning REMINDER: should probably define a modeflags type
#endif
unsigned short bufmask = __FLAG_LBF;
#ifndef NDEBUG
if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) {
__STDIO_STREAM_VALIDATE(stream); /* debugging only */
}
#endif
if (stream == (FILE *) &_stdio_openlist) { /* Flush all lbf streams. */
stream = NULL;
bufmask = 0;
}
if (!stream) { /* Flush all (lbf) writing streams. */
__STDIO_OPENLIST_INC_USE;
__STDIO_THREADLOCK_OPENLIST_ADD;
stream = _stdio_openlist;
__STDIO_THREADUNLOCK_OPENLIST_ADD;
while(stream) {
/* We only care about currently writing streams and do not want to
* block trying to obtain mutexes on non-writing streams. */
#warning fix for nonatomic
#warning unnecessary check if no threads
if (__STDIO_STREAM_IS_WRITING(stream)) { /* ONLY IF ATOMIC!!! */
__MY_STDIO_THREADLOCK(stream);
/* Need to check again once we have the lock. */
if (!(((stream->__modeflags | bufmask)
^ (__FLAG_WRITING|__FLAG_LBF)
) & (__FLAG_WRITING|__MASK_BUFMODE))
) {
if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) {
__STDIO_STREAM_DISABLE_PUTC(stream);
__STDIO_STREAM_CLEAR_WRITING(stream);
} else {
retval = EOF;
}
}
__MY_STDIO_THREADUNLOCK(stream);
}
stream = stream->__nextopen;
}
__STDIO_OPENLIST_DEC_USE;
} else if (__STDIO_STREAM_IS_WRITING(stream)) {
if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) {
__STDIO_STREAM_DISABLE_PUTC(stream);
__STDIO_STREAM_CLEAR_WRITING(stream);
} else {
retval = EOF;
}
}
#if 0
else if (stream->__modeflags & (__MASK_READING|__FLAG_READONLY)) {
/* ANSI/ISO says behavior in this case is undefined but also says you
* shouldn't flush a stream you were reading from. As usual, glibc
* caters to broken programs and simply ignores this. */
__UNDEFINED_OR_NONPORTABLE;
__STDIO_STREAM_SET_ERROR(stream);
__set_errno(EBADF);
retval = EOF;
}
#endif
#ifndef NDEBUG
if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) {
__STDIO_STREAM_VALIDATE(stream); /* debugging only */
}
#endif
return retval;
#else /* __STDIO_BUFFERS --------------------------------------- */
#ifndef NDEBUG
if ((stream != NULL)
#ifdef __STDIO_HAS_OPENLIST
&& (stream != (FILE *) &_stdio_openlist)
#endif
) {
__STDIO_STREAM_VALIDATE(stream); /* debugging only */
}
#endif
#if 0
if (stream && (stream->__modeflags & (__MASK_READING|__FLAG_READONLY))) {
/* ANSI/ISO says behavior in this case is undefined but also says you
* shouldn't flush a stream you were reading from. As usual, glibc
* caters to broken programs and simply ignores this. */
__UNDEFINED_OR_NONPORTABLE;
__STDIO_STREAM_SET_ERROR(stream);
__set_errno(EBADF);
return EOF;
}
#endif
return 0;
#endif /* __STDIO_BUFFERS */
}
libc_hidden_def(fflush_unlocked)
#ifndef __UCLIBC_HAS_THREADS__
strong_alias(fflush_unlocked,fflush)
libc_hidden_def(fflush)
#endif
#elif defined __UCLIBC_HAS_THREADS__
int fflush(register FILE *stream)
{
int retval;
__STDIO_AUTO_THREADLOCK_VAR;
if (stream
#ifdef __STDIO_HAS_OPENLIST
&& (stream != (FILE *) &_stdio_openlist)
#endif
) {
__STDIO_AUTO_THREADLOCK(stream);
retval = fflush_unlocked(stream);
__STDIO_AUTO_THREADUNLOCK(stream);
} else {
retval = fflush_unlocked(stream);
}
return retval;
}
libc_hidden_def(fflush)
#endif