[libcxx-commits] [libcxx] [libc++] Always disable buffering of the stream underlying std::filebuf (PR #209872)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 20 01:41:03 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209872
>From 71721e6a480427a3410333e46cb62cd58cc30c39 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 15 Jul 2026 21:40:13 +0200
Subject: [PATCH] [libc++] Always disable buffering of the stream underlying
std::filebuf
---
libcxx/docs/ReleaseNotes/24.rst | 3 +-
libcxx/include/fstream | 70 +++----------------
.../fstreams/filebuf.virtuals/setbuf.pass.cpp | 4 +-
3 files changed, 14 insertions(+), 63 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..163a8cc6c37db 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -42,6 +42,8 @@ Implemented Papers
Improvements and New Features
-----------------------------
+- ``std::fstream`` disables buffering of the underlying stream now to avoid double buffering and to better respect the
+ users requests, improving performance by up to 2x.
Deprecations and Removals
-------------------------
@@ -71,4 +73,3 @@ ABI Affecting Changes
Build System Changes
--------------------
-
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..6c14978b6b1b5 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -341,43 +341,7 @@ private:
state_type __st_;
state_type __st_last_;
ios_base::openmode __om_;
- // There have been no file operations yet, which allows setting unbuffered
- // I/O mode.
- static const ios_base::openmode __no_io_operations = ios_base::trunc;
- // Unbuffered I/O mode has been requested.
- static const ios_base::openmode __use_unbuffered_io = ios_base::ate;
- // Used to track the currently used mode and track whether the output should
- // be unbuffered.
- // [filebuf.virtuals]/12
- // If setbuf(0, 0) is called on a stream before any I/O has occurred on
- // that stream, the stream becomes unbuffered. Otherwise the results are
- // implementation-defined.
- // This allows calling setbuf(0, 0)
- // - before opening a file,
- // - after opening a file, before
- // - a read
- // - a write
- // - a seek.
- // Note that opening a file with ios_base::ate does a seek operation.
- // Normally underflow, overflow, and sync change this flag to ios_base::in,
- // ios_base_out, or 0.
- //
- // The ios_base::trunc and ios_base::ate flags are not used in __cm_. They
- // are used to track the state of the unbuffered request. For readability
- // they have the aliases __no_io_operations and __use_unbuffered_io
- // respectively.
- //
- // The __no_io_operations and __use_unbuffered_io flags are used in the
- // following way:
- // - __no_io_operations is set upon construction to indicate the unbuffered
- // state can be set.
- // - When requesting unbuffered output:
- // - If the file is open it sets the mode.
- // - Else places a request by adding the __use_unbuffered_io flag.
- // - When a file is opened it checks whether both __no_io_operations and
- // __use_unbuffered_io are set. If so switches to unbuffered mode.
- // - All file I/O operations change the mode effectively clearing the
- // __no_io_operations and __use_unbuffered_io flags.
+ // Used to track the currently used mode.
ios_base::openmode __cm_;
bool __owns_eb_;
bool __owns_ib_;
@@ -400,11 +364,6 @@ private:
return nullptr;
__om_ = __mode;
- if (__cm_ == (__no_io_operations | __use_unbuffered_io)) {
- std::setbuf(__file_, nullptr);
- __cm_ = 0;
- }
-
if (__mode & ios_base::ate) {
__cm_ = 0;
if (fseek(__file_, 0, SEEK_END)) {
@@ -417,18 +376,6 @@ private:
return this;
}
- // If the file is already open, switch to unbuffered mode. Otherwise, record
- // the request to use unbuffered mode so that we use that mode when we
- // eventually open the file.
- _LIBCPP_HIDE_FROM_ABI void __request_unbuffered_mode() {
- if (__file_) {
- std::setbuf(__file_, nullptr);
- __cm_ = 0;
- } else {
- __cm_ = __no_io_operations | __use_unbuffered_io;
- }
- }
-
_LIBCPP_HIDE_FROM_ABI typename traits_type::int_type __overflow_failed() {
if (this->pptr() == this->epptr() + 1) {
this->pbump(-1); // lose the character we overflowed above -- we don't really have a
@@ -451,7 +398,7 @@ basic_filebuf<_CharT, _Traits>::basic_filebuf()
__st_(),
__st_last_(),
__om_(0),
- __cm_(__no_io_operations),
+ __cm_(),
__owns_eb_(false),
__owns_ib_(false),
__always_noconv_(false) {
@@ -742,7 +689,12 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
if (!__mdstr)
return nullptr;
- return __do_open(std::fopen(__s, __mdstr), __mode);
+ if (FILE* __file = std::fopen(__s, __mdstr)) {
+ // We already buffer - no need to buffer twice
+ std::setbuf(__file, nullptr);
+ return __do_open(__file, __mode);
+ }
+ return nullptr;
}
template <class _CharT, class _Traits>
@@ -793,7 +745,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::close() {
// since the user may re-open the stream.
this->setg(nullptr, nullptr, nullptr);
this->setp(nullptr, nullptr);
- __cm_ = __no_io_operations;
+ __cm_ = 0;
}
return __rt;
}
@@ -949,9 +901,7 @@ template <class _CharT, class _Traits>
basic_streambuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::setbuf(char_type* __s, streamsize __n) {
this->setg(nullptr, nullptr, nullptr);
this->setp(nullptr, nullptr);
- // Calling setbuf(nullptr, 0) before any i/o operation switches the stream to unbuffered mode
- if (__cm_ == __no_io_operations && __s == nullptr && __n == 0)
- __request_unbuffered_mode();
+ __cm_ = 0;
if (__owns_eb_)
delete[] __extbuf_;
if (__owns_ib_)
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp
index 72af0a2db1180..f1f762c095485 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp
@@ -97,8 +97,8 @@ static void unbuffered_request_after_open_ate() {
buffer.sputc(CharT('a'));
assert(file_size<CharT>("test.dat") <= 1);
- // on libc++ buffering is used by default.
- LIBCPP_ASSERT(file_size<CharT>("test.dat") == 0);
+ // libc++ doesn't buffer if the buffer is explicitly removed.
+ LIBCPP_NON_FROZEN_ASSERT(file_size<CharT>("test.dat") == 1);
buffer.close();
assert(file_size<CharT>("test.dat") == 1);
More information about the libcxx-commits
mailing list