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)29 void 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)68 int 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