1 /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> 2 * 3 * GNU Library General Public License (LGPL) version 2 or later. 4 * 5 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. 6 */ 7 8 #include "_stdio.h" 9 10 11 #ifdef __DO_UNLOCKED 12 13 #ifdef __UCLIBC_MJN3_ONLY__ 14 #warning WISHLIST: Add option to test for undefined behavior of fflush. 15 #endif /* __UCLIBC_MJN3_ONLY__ */ 16 17 /* Even if the stream is set to user-locking, we still need to lock 18 * when all (lbf) writing streams are flushed. */ 19 20 #define __MY_STDIO_THREADLOCK(__stream) \ 21 __UCLIBC_IO_MUTEX_CONDITIONAL_LOCK((__stream)->__lock, \ 22 (_stdio_user_locking != 2)) 23 24 #define __MY_STDIO_THREADUNLOCK(__stream) \ 25 __UCLIBC_IO_MUTEX_CONDITIONAL_UNLOCK((__stream)->__lock, \ 26 (_stdio_user_locking != 2)) 27 28 #if defined(__UCLIBC_HAS_THREADS__) && defined(__STDIO_BUFFERS) _stdio_openlist_dec_use(void)29void attribute_hidden _stdio_openlist_dec_use(void) 30 { 31 __STDIO_THREADLOCK_OPENLIST_DEL; 32 if ((_stdio_openlist_use_count == 1) && (_stdio_openlist_del_count > 0)) { 33 FILE *p = NULL; 34 FILE *n; 35 FILE *stream; 36 37 #ifdef __UCLIBC_MJN3_ONLY__ 38 #warning REMINDER: As an optimization, we could unlock after we move past the head. 39 #endif 40 /* Grab the openlist add lock since we might change the head of the list. */ 41 __STDIO_THREADLOCK_OPENLIST_ADD; 42 for (stream = _stdio_openlist; stream; stream = n) { 43 n = stream->__nextopen; 44 #ifdef __UCLIBC_MJN3_ONLY__ 45 #warning REMINDER: fix for nonatomic 46 #endif 47 if ((stream->__modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY|__FLAG_FAILED_FREOPEN)) 48 == (__FLAG_READONLY|__FLAG_WRITEONLY) 49 ) { /* The file was closed and should be removed from the list. */ 50 if (!p) { 51 _stdio_openlist = n; 52 } else { 53 p->__nextopen = n; 54 } 55 __STDIO_STREAM_FREE_FILE(stream); 56 } else { 57 p = stream; 58 } 59 } 60 __STDIO_THREADUNLOCK_OPENLIST_ADD; 61 _stdio_openlist_del_count = 0; /* Should be clean now. */ 62 } 63 --_stdio_openlist_use_count; 64 __STDIO_THREADUNLOCK_OPENLIST_DEL; 65 } 66 #endif 67 fflush_unlocked(register FILE * stream)68int fflush_unlocked(register FILE *stream) 69 { 70 #ifdef __STDIO_BUFFERS 71 72 int retval = 0; 73 #ifdef __UCLIBC_MJN3_ONLY__ 74 #warning REMINDER: should probably define a modeflags type 75 #endif 76 unsigned short bufmask = __FLAG_LBF; 77 78 #ifndef NDEBUG 79 if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { 80 __STDIO_STREAM_VALIDATE(stream); /* debugging only */ 81 } 82 #endif 83 84 if (stream == (FILE *) &_stdio_openlist) { /* Flush all lbf streams. */ 85 stream = NULL; 86 bufmask = 0; 87 } 88 89 if (!stream) { /* Flush all (lbf) writing streams. */ 90 91 __STDIO_OPENLIST_INC_USE; 92 93 __STDIO_THREADLOCK_OPENLIST_ADD; 94 stream = _stdio_openlist; 95 __STDIO_THREADUNLOCK_OPENLIST_ADD; 96 97 while(stream) { 98 /* We only care about currently writing streams and do not want to 99 * block trying to obtain mutexes on non-writing streams. */ 100 #warning fix for nonatomic 101 #warning unnecessary check if no threads 102 if (__STDIO_STREAM_IS_WRITING(stream)) { /* ONLY IF ATOMIC!!! */ 103 __MY_STDIO_THREADLOCK(stream); 104 /* Need to check again once we have the lock. */ 105 if (!(((stream->__modeflags | bufmask) 106 ^ (__FLAG_WRITING|__FLAG_LBF) 107 ) & (__FLAG_WRITING|__MASK_BUFMODE)) 108 ) { 109 if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) { 110 __STDIO_STREAM_DISABLE_PUTC(stream); 111 __STDIO_STREAM_CLEAR_WRITING(stream); 112 } else { 113 retval = EOF; 114 } 115 } 116 __MY_STDIO_THREADUNLOCK(stream); 117 } 118 stream = stream->__nextopen; 119 } 120 121 __STDIO_OPENLIST_DEC_USE; 122 123 } else if (__STDIO_STREAM_IS_WRITING(stream)) { 124 if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) { 125 __STDIO_STREAM_DISABLE_PUTC(stream); 126 __STDIO_STREAM_CLEAR_WRITING(stream); 127 } else { 128 retval = EOF; 129 } 130 } 131 #if 0 132 else if (stream->__modeflags & (__MASK_READING|__FLAG_READONLY)) { 133 /* ANSI/ISO says behavior in this case is undefined but also says you 134 * shouldn't flush a stream you were reading from. As usual, glibc 135 * caters to broken programs and simply ignores this. */ 136 __UNDEFINED_OR_NONPORTABLE; 137 __STDIO_STREAM_SET_ERROR(stream); 138 __set_errno(EBADF); 139 retval = EOF; 140 } 141 #endif 142 143 #ifndef NDEBUG 144 if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { 145 __STDIO_STREAM_VALIDATE(stream); /* debugging only */ 146 } 147 #endif 148 149 return retval; 150 151 #else /* __STDIO_BUFFERS --------------------------------------- */ 152 153 #ifndef NDEBUG 154 if ((stream != NULL) 155 #ifdef __STDIO_HAS_OPENLIST 156 && (stream != (FILE *) &_stdio_openlist) 157 #endif 158 ) { 159 __STDIO_STREAM_VALIDATE(stream); /* debugging only */ 160 } 161 #endif 162 163 #if 0 164 if (stream && (stream->__modeflags & (__MASK_READING|__FLAG_READONLY))) { 165 /* ANSI/ISO says behavior in this case is undefined but also says you 166 * shouldn't flush a stream you were reading from. As usual, glibc 167 * caters to broken programs and simply ignores this. */ 168 __UNDEFINED_OR_NONPORTABLE; 169 __STDIO_STREAM_SET_ERROR(stream); 170 __set_errno(EBADF); 171 return EOF; 172 } 173 #endif 174 175 return 0; 176 #endif /* __STDIO_BUFFERS */ 177 } 178 libc_hidden_def(fflush_unlocked) 179 180 #ifndef __UCLIBC_HAS_THREADS__ 181 strong_alias(fflush_unlocked,fflush) 182 libc_hidden_def(fflush) 183 #endif 184 185 #elif defined __UCLIBC_HAS_THREADS__ 186 187 int fflush(register FILE *stream) 188 { 189 int retval; 190 __STDIO_AUTO_THREADLOCK_VAR; 191 192 if (stream 193 #ifdef __STDIO_HAS_OPENLIST 194 && (stream != (FILE *) &_stdio_openlist) 195 #endif 196 ) { 197 198 __STDIO_AUTO_THREADLOCK(stream); 199 200 retval = fflush_unlocked(stream); 201 202 __STDIO_AUTO_THREADUNLOCK(stream); 203 } else { 204 retval = fflush_unlocked(stream); 205 } 206 207 return retval; 208 } 209 libc_hidden_def(fflush) 210 211 #endif 212