libc: fix MIPS N64 fork
[uclibc-ng.git] / libc / stdio / fflush.c
blobd9104a42f2c233f0e5a08739f01731de337e1e4f
1 /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
3 * GNU Library General Public License (LGPL) version 2 or later.
5 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
6 */
8 #include "_stdio.h"
11 #ifdef __DO_UNLOCKED
13 #ifdef __UCLIBC_MJN3_ONLY__
14 #warning WISHLIST: Add option to test for undefined behavior of fflush.
15 #endif /* __UCLIBC_MJN3_ONLY__ */
17 /* Even if the stream is set to user-locking, we still need to lock
18 * when all (lbf) writing streams are flushed. */
20 #define __MY_STDIO_THREADLOCK(__stream) \
21 __UCLIBC_IO_MUTEX_CONDITIONAL_LOCK((__stream)->__lock, \
22 (_stdio_user_locking != 2))
24 #define __MY_STDIO_THREADUNLOCK(__stream) \
25 __UCLIBC_IO_MUTEX_CONDITIONAL_UNLOCK((__stream)->__lock, \
26 (_stdio_user_locking != 2))
28 #if defined(__UCLIBC_HAS_THREADS__) && defined(__STDIO_BUFFERS)
29 void attribute_hidden _stdio_openlist_dec_use(void)
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;
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;
55 __STDIO_STREAM_FREE_FILE(stream);
56 } else {
57 p = stream;
60 __STDIO_THREADUNLOCK_OPENLIST_ADD;
61 _stdio_openlist_del_count = 0; /* Should be clean now. */
63 --_stdio_openlist_use_count;
64 __STDIO_THREADUNLOCK_OPENLIST_DEL;
66 #endif
68 int fflush_unlocked(register FILE *stream)
70 #ifdef __STDIO_BUFFERS
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;
78 #ifndef NDEBUG
79 if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) {
80 __STDIO_STREAM_VALIDATE(stream); /* debugging only */
82 #endif
84 if (stream == (FILE *) &_stdio_openlist) { /* Flush all lbf streams. */
85 stream = NULL;
86 bufmask = 0;
89 if (!stream) { /* Flush all (lbf) writing streams. */
91 __STDIO_OPENLIST_INC_USE;
93 __STDIO_THREADLOCK_OPENLIST_ADD;
94 stream = _stdio_openlist;
95 __STDIO_THREADUNLOCK_OPENLIST_ADD;
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))
109 if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) {
110 __STDIO_STREAM_DISABLE_PUTC(stream);
111 __STDIO_STREAM_CLEAR_WRITING(stream);
112 } else {
113 retval = EOF;
116 __MY_STDIO_THREADUNLOCK(stream);
118 stream = stream->__nextopen;
121 __STDIO_OPENLIST_DEC_USE;
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;
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;
141 #endif
143 #ifndef NDEBUG
144 if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) {
145 __STDIO_STREAM_VALIDATE(stream); /* debugging only */
147 #endif
149 return retval;
151 #else /* __STDIO_BUFFERS --------------------------------------- */
153 #ifndef NDEBUG
154 if ((stream != NULL)
155 #ifdef __STDIO_HAS_OPENLIST
156 && (stream != (FILE *) &_stdio_openlist)
157 #endif
159 __STDIO_STREAM_VALIDATE(stream); /* debugging only */
161 #endif
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;
173 #endif
175 return 0;
176 #endif /* __STDIO_BUFFERS */
178 libc_hidden_def(fflush_unlocked)
180 #ifndef __UCLIBC_HAS_THREADS__
181 strong_alias(fflush_unlocked,fflush)
182 libc_hidden_def(fflush)
183 #endif
185 #elif defined __UCLIBC_HAS_THREADS__
187 int fflush(register FILE *stream)
189 int retval;
190 __STDIO_AUTO_THREADLOCK_VAR;
192 if (stream
193 #ifdef __STDIO_HAS_OPENLIST
194 && (stream != (FILE *) &_stdio_openlist)
195 #endif
198 __STDIO_AUTO_THREADLOCK(stream);
200 retval = fflush_unlocked(stream);
202 __STDIO_AUTO_THREADUNLOCK(stream);
203 } else {
204 retval = fflush_unlocked(stream);
207 return retval;
209 libc_hidden_def(fflush)
211 #endif