]> git.ipfire.org Git - thirdparty/gcc.git/commit
libstdc++: Fix data race in std::basic_ios::fill() [PR77704]
authorJonathan Wakely <jwakely@redhat.com>
Fri, 3 May 2024 19:00:08 +0000 (20:00 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 15 May 2024 11:17:34 +0000 (12:17 +0100)
commit23ef0f68ad5fca1fd7027caaaa5f6cb9f6d27b28
treee3becfef4c17c778d3d026ef2404016bee93a4c9
parenta0e9fde8766a5b5a28a76c3a6fb0276efa47cd6d
libstdc++: Fix data race in std::basic_ios::fill() [PR77704]

The lazy caching in std::basic_ios::fill() updates a mutable member
without synchronization, which can cause a data race if two threads both
call fill() on the same stream object when _M_fill_init is false.

To avoid this we can just cache the _M_fill member and set _M_fill_init
early in std::basic_ios::init, instead of doing it lazily. As explained
by the comment in init, there's a good reason for doing it lazily. When
char_type is neither char nor wchar_t, the locale might not have a
std::ctype<char_type> facet, so getting the fill character would throw
an exception. The current lazy init allows using unformatted I/O with
such a stream, because the fill character is never needed and so it
doesn't matter if the locale doesn't have a ctype<char_type> facet. We
can maintain this property by only setting the fill character in
std::basic_ios::init if the ctype facet is present at that time. If
fill() is called later and the fill character wasn't set by init, we can
get it from the stream's current locale at the point when fill() is
called (and not try to cache it without synchronization). If the stream
hasn't been imbued with a locale that includes the facet when we need
the fill() character, then throw bad_cast at that point.

This causes a change in behaviour for the following program:

  std::ostringstream out;
  out.imbue(loc);
  auto fill = out.fill();

Previously the fill character would have been set when fill() is called,
and so would have used the new locale. This commit changes it so that
the fill character is set on construction and isn't affected by the new
locale being imbued later. This new behaviour seems to be what the
standard requires, and matches MSVC.

The new 27_io/basic_ios/fill/char/fill.cc test verifies that it's still
possible to use a std::basic_ios without the ctype<char_type> facet
being present at construction.

libstdc++-v3/ChangeLog:

PR libstdc++/77704
* include/bits/basic_ios.h (basic_ios::fill()): Do not modify
_M_fill and _M_fill_init in a const member function.
(basic_ios::fill(char_type)): Use _M_fill directly instead of
calling fill(). Set _M_fill_init to true.
* include/bits/basic_ios.tcc (basic_ios::init): Set _M_fill and
_M_fill_init here instead.
* testsuite/27_io/basic_ios/fill/char/1.cc: New test.
* testsuite/27_io/basic_ios/fill/wchar_t/1.cc: New test.
libstdc++-v3/include/bits/basic_ios.h
libstdc++-v3/include/bits/basic_ios.tcc
libstdc++-v3/testsuite/27_io/basic_ios/fill/char/1.cc [new file with mode: 0644]
libstdc++-v3/testsuite/27_io/basic_ios/fill/wchar_t/1.cc [new file with mode: 0644]