1 // 2 // fflush.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Defines fflush() and related functions, which flush stdio streams. 7 // 8 #include <corecrt_internal_stdio.h> 9 #include <corecrt_internal_ptd_propagation.h> 10 11 12 13 static bool __cdecl is_stream_allocated(long const stream_flags) throw() 14 { 15 return (stream_flags & _IOALLOCATED) != 0; 16 } 17 18 static bool __cdecl is_stream_flushable(long const stream_flags) throw() 19 { 20 if ((stream_flags & (_IOREAD | _IOWRITE)) != _IOWRITE) 21 { 22 return false; 23 } 24 25 if ((stream_flags & (_IOBUFFER_CRT | _IOBUFFER_USER)) == 0) 26 { 27 return false; 28 } 29 30 return true; 31 } 32 33 static bool __cdecl is_stream_flushable_or_commitable(long const stream_flags) throw() 34 { 35 if (is_stream_flushable(stream_flags)) 36 { 37 return true; 38 } 39 40 if (stream_flags & _IOCOMMIT) 41 { 42 return true; 43 } 44 45 return false; 46 } 47 48 // Returns true if the common_flush_all function should attempt to flush the 49 // stream; otherwise returns false. This function returns false for streams 50 // that are not in use (not allocated) and for streams for which the flush 51 // operation will be a no-op. 52 // 53 // In the case where this function determines that the flush would be a no-op, 54 // it increments the flushed_stream_count. This allows common_flush_all to 55 // keep track of the number of streams that it would have flushed. 56 static bool __cdecl common_flush_all_should_try_to_flush_stream( 57 _In_ __crt_stdio_stream const stream, 58 _Inout_ int* const flushed_stream_count 59 ) throw() 60 { 61 if (!stream.valid()) 62 { 63 return false; 64 } 65 66 long const stream_flags = stream.get_flags(); 67 if (!is_stream_allocated(stream_flags)) 68 { 69 return false; 70 } 71 72 if (!is_stream_flushable_or_commitable(stream_flags)) 73 { 74 ++*flushed_stream_count; 75 return false; 76 } 77 78 return true; 79 } 80 81 82 83 // Internal implementation of the "flush all" functionality. If the 84 // flush_read_mode_streams argument is false, only write mode streams are 85 // flushed and the return value is zero on success, EOF on failure. 86 // 87 // If the flush_read_mode_streams argument is true, this function flushes 88 // all streams regardless of mode and returns the number of streams that it 89 // flushed. 90 // 91 // Note that in both cases, if we can determine that a call to fflush for a 92 // particular stream would be a no-op, then we will not call fflush for that 93 // stream. This allows us to avoid acquiring the stream lock unnecessarily, 94 // which can help to avoid deadlock-like lock contention. 95 // 96 // Notably, by doing this, we can avoid attempting to acquire the stream lock 97 // for most read mode streams. Attempting to acquire the stream lock for a 98 // read mode stream can be problematic beause another thread may hold the lock 99 // and be blocked on an I/O operation (e.g., a call to fread on stdin may block 100 // until the user types input into the console). 101 static int __cdecl common_flush_all(bool const flush_read_mode_streams) throw() 102 { 103 int count = 0; 104 int error = 0; 105 106 __acrt_lock_and_call(__acrt_stdio_index_lock, [&] 107 { 108 __crt_stdio_stream_data** const first_file = __piob; 109 __crt_stdio_stream_data** const last_file = first_file + _nstream; 110 111 for (__crt_stdio_stream_data** it = first_file; it != last_file; ++it) 112 { 113 __crt_stdio_stream const stream(*it); 114 115 // Before we acquire the stream lock, check to see if flushing the 116 // stream would be a no-op. If it would be, then skip this stream. 117 if (!common_flush_all_should_try_to_flush_stream(stream, &count)) 118 { 119 continue; 120 } 121 122 __acrt_lock_stream_and_call(stream.public_stream(), [&] 123 { 124 // Re-verify the state of the stream. Another thread may have 125 // closed the stream, reopened it into a different mode, or 126 // otherwise altered the state of the stream such that this 127 // flush would be a no-op. 128 if (!common_flush_all_should_try_to_flush_stream(stream, &count)) 129 { 130 return; 131 } 132 133 if (!flush_read_mode_streams && !stream.has_all_of(_IOWRITE)) 134 { 135 return; 136 } 137 138 if (_fflush_nolock(stream.public_stream()) != EOF) 139 { 140 ++count; 141 } 142 else 143 { 144 error = EOF; 145 } 146 }); 147 } 148 }); 149 150 return flush_read_mode_streams ? count : error; 151 } 152 153 154 155 // Flushes the buffer of the given stream. If the file is open for writing and 156 // is buffered, the buffer is flushed. On success, returns 0. On failure (e.g. 157 // if there is an error writing the buffer), returns EOF and sets errno. 158 extern "C" int __cdecl fflush(FILE* const public_stream) 159 { 160 __crt_stdio_stream const stream(public_stream); 161 162 // If the stream is null, flush all the streams: 163 if (!stream.valid()) 164 { 165 return common_flush_all(false); 166 } 167 168 // Before acquiring the stream lock, inspect the stream to see if the flush 169 // is a no-op. If it will be a no-op then we can return without attempting 170 // to acquire the lock (this can help prevent locking conflicts; see the 171 // common_flush_all implementation for more information). 172 if (!is_stream_flushable_or_commitable(stream.get_flags())) 173 { 174 return 0; 175 } 176 177 return __acrt_lock_stream_and_call(stream.public_stream(), [&] 178 { 179 return _fflush_nolock(stream.public_stream()); 180 }); 181 } 182 183 184 185 static int __cdecl _fflush_nolock_internal(FILE* const public_stream, __crt_cached_ptd_host& ptd) 186 { 187 __crt_stdio_stream const stream(public_stream); 188 189 // If the stream is null, flush all the streams. 190 if (!stream.valid()) 191 { 192 return common_flush_all(false); 193 } 194 195 if (__acrt_stdio_flush_nolock(stream.public_stream(), ptd) != 0) 196 { 197 // If the flush fails, do not attempt to commit: 198 return EOF; 199 } 200 201 // Perform the lowio commit to ensure data is written to disk: 202 if (stream.has_all_of(_IOCOMMIT)) 203 { 204 if (_commit(_fileno(public_stream))) 205 { 206 return EOF; 207 } 208 } 209 210 return 0; 211 } 212 213 extern "C" int __cdecl _fflush_nolock(FILE* const public_stream) 214 { 215 __crt_cached_ptd_host ptd; 216 return _fflush_nolock_internal(public_stream, ptd); 217 } 218 219 // Flushes the buffer of the given stream. If the file is open for writing and 220 // is buffered, the buffer is flushed. On success, returns 0. On failure (e.g. 221 // if there is an error writing the buffer), returns EOF and sets errno. 222 extern "C" int __cdecl __acrt_stdio_flush_nolock(FILE* const public_stream, __crt_cached_ptd_host& ptd) 223 { 224 __crt_stdio_stream const stream(public_stream); 225 226 if (!is_stream_flushable(stream.get_flags())) 227 { 228 return 0; 229 } 230 231 int const bytes_to_write = static_cast<int>(stream->_ptr - stream->_base); 232 233 __acrt_stdio_reset_buffer(stream); 234 235 if (bytes_to_write <= 0) 236 { 237 return 0; 238 } 239 240 int const bytes_written = _write_internal(_fileno(stream.public_stream()), stream->_base, bytes_to_write, ptd); 241 if (bytes_to_write != bytes_written) 242 { 243 stream.set_flags(_IOERROR); 244 return EOF; 245 } 246 247 // If this is a read/write file, clear _IOWRITE so that the next operation can 248 // be a read: 249 if (stream.has_all_of(_IOUPDATE)) 250 { 251 stream.unset_flags(_IOWRITE); 252 } 253 254 return 0; 255 } 256 257 258 259 // Flushes the buffers for all output streams and clears all input buffers. 260 // Returns the number of open streams. 261 extern "C" int __cdecl _flushall() 262 { 263 return common_flush_all(true); 264 } 265