/* 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