diff --git a/libstdc++-v3/include/bits/ostream.h b/libstdc++-v3/include/bits/ostream.h index 8ee63d2d66e54657c7c440bc3afb8147226761e1..d19a76ab2474c774fb0dc2a1ff85f5cb0b68c3d7 100644 --- a/libstdc++-v3/include/bits/ostream.h +++ b/libstdc++-v3/include/bits/ostream.h @@ -507,6 +507,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __d; } #pragma GCC diagnostic pop + + // RAII type to clear and restore an ostream's exceptions mask. + struct _Disable_exceptions + { + _Disable_exceptions(basic_ostream& __os) + : _M_os(__os), _M_exception(_M_os._M_exception) + { _M_os._M_exception = ios_base::goodbit; } + + ~_Disable_exceptions() + { _M_os._M_exception = _M_exception; } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++11-extensions" // deleted functions + _Disable_exceptions(const _Disable_exceptions&) = delete; + _Disable_exceptions& operator=(const _Disable_exceptions&) = delete; +#pragma GCC diagnostic pop + + private: + basic_ostream& _M_os; + const ios_base::iostate _M_exception; + }; }; /** @@ -543,18 +564,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /** * @brief Possibly flushes the stream. * - * If @c ios_base::unitbuf is set in @c os.flags(), and - * @c std::uncaught_exception() is true, the sentry destructor calls - * @c flush() on the output stream. + * If `ios_base::unitbuf` is set in `os.flags()`, and + * `std::uncaught_exception()` is true, the sentry destructor flushes + * the output stream. */ ~sentry() { - // XXX MT - if (bool(_M_os.flags() & ios_base::unitbuf) && !uncaught_exception()) + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 397. ostream::sentry dtor throws exceptions + // 835. Tying two streams together (correction to DR 581) + // 4188. ostream::sentry destructor should handle exceptions + if (bool(_M_os.flags() & ios_base::unitbuf) && _M_os.good() + && !uncaught_exception()) // XXX MT { - // Can't call flush directly or else will get into recursive lock. - if (_M_os.rdbuf() && _M_os.rdbuf()->pubsync() == -1) - _M_os.setstate(ios_base::badbit); + _Disable_exceptions __noex(_M_os); + __try + { + // Can't call _M_os.flush() directly because that constructs + // another sentry. + if (_M_os.rdbuf() && _M_os.rdbuf()->pubsync() == -1) + _M_os.setstate(ios_base::badbit); + } + __catch(...) + { _M_os.setstate(ios_base::badbit); } } } #pragma GCC diagnostic pop diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/char/lwg4188.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/char/lwg4188.cc new file mode 100644 index 0000000000000000000000000000000000000000..4a7326665368134d8c7a996e6b49a8a64900cc76 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/char/lwg4188.cc @@ -0,0 +1,50 @@ +// { dg-do run } + +// 4188. ostream::sentry destructor should handle exceptions + +#include <ostream> +#include <streambuf> +#include <testsuite_hooks.h> + +struct bad_streambuf : std::streambuf +{ + int sync() { return -1; } +}; + +void +test_returns_error() +{ + bad_streambuf buf; + std::ostream out(&buf); + out.setf(std::ios::unitbuf); + out.exceptions(std::ios::badbit); + out.write("", 0); // constructs sentry + VERIFY( out.bad() ); +} + +struct exceptionally_bad_streambuf : std::streambuf +{ + int sync() { throw std::ios::failure("unsyncable"); } +}; + +void +test_throws() +{ + exceptionally_bad_streambuf buf; + std::ostream out(&buf); + out.setf(std::ios::unitbuf); + out.write("", 0); // constructs sentry + VERIFY( out.bad() ); + + // Repeat with badbit in exceptions mask + out.clear(); + out.exceptions(std::ios::badbit); + out.write("", 0); // constructs sentry + VERIFY( out.bad() ); +} + +int main() +{ + test_returns_error(); + test_throws(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/wchar_t/lwg4188.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/wchar_t/lwg4188.cc new file mode 100644 index 0000000000000000000000000000000000000000..a3354e131dc597480b319db6e12329383904c612 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/exceptions/wchar_t/lwg4188.cc @@ -0,0 +1,50 @@ +// { dg-do run } + +// 4188. ostream::sentry destructor should handle exceptions + +#include <ostream> +#include <streambuf> +#include <testsuite_hooks.h> + +struct bad_streambuf : std::wstreambuf +{ + int sync() { return -1; } +}; + +void +test_returns_error() +{ + bad_streambuf buf; + std::wostream out(&buf); + out.setf(std::wios::unitbuf); + out.exceptions(std::wios::badbit); + out.write(L"", 0); // constructs sentry + VERIFY( out.bad() ); +} + +struct exceptionally_bad_streambuf : std::wstreambuf +{ + int sync() { throw std::wios::failure("unsyncable"); } +}; + +void +test_throws() +{ + exceptionally_bad_streambuf buf; + std::wostream out(&buf); + out.setf(std::wios::unitbuf); + out.write(L"", 0); // constructs sentry + VERIFY( out.bad() ); + + // Repeat with badbit in exceptions mask + out.clear(); + out.exceptions(std::wios::badbit); + out.write(L"", 0); // constructs sentry + VERIFY( out.bad() ); +} + +int main() +{ + test_returns_error(); + test_throws(); +}