blob: d9104a42f2c233f0e5a08739f01731de337e1e4f [file] [log] [blame]
yuezonghe824eb0c2024-06-27 02:32:26 -07001/* 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)
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
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}
178libc_hidden_def(fflush_unlocked)
179
180#ifndef __UCLIBC_HAS_THREADS__
181strong_alias(fflush_unlocked,fflush)
182libc_hidden_def(fflush)
183#endif
184
185#elif defined __UCLIBC_HAS_THREADS__
186
187int 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}
209libc_hidden_def(fflush)
210
211#endif