Opened 7 years ago
Last modified 5 years ago
#11633 assigned Support Requests
chrono IO V1 may throw bad_cast
Reported by: | Owned by: | viboes | |
---|---|---|---|
Milestone: | To Be Determined | Component: | chrono |
Version: | Boost 1.58.0 | Severity: | Problem |
Keywords: | Cc: |
Description
I had occurences of chrono io throwing bad_cast exceptions, which lead to backtraces like this one:
#0 0x00007ffff54fda30 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #1 0x00007ffff554f2a2 in std::__throw_bad_cast() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #2 0x00007ffff2289bab in std::use_facet<boost::chrono::duration_punct<char> > (__loc=...) at /usr/include/c++/4.8/bits/locale_classes.tcc:137 #3 0x00007ffff2288c61 in boost::chrono::operator<< <char, std::char_traits<char>, boost::rational<int>, boost::ratio<1l, 1l> > (os=..., d=...) at /home/sbarthelemy/.local/share/qi/toolchains/linux64/boost/include/boost/chrono/io_v1/chrono_io.hpp:210
I think the attached minimal example reproduces the issue.
Here the compile & run log using boost 1.58
$ make clean && make test rm -f src/*.o src/chrono_io clang++ -std=c++11 -c -g -fPIC -Iinclude -I/usr/include -o src/chrono_io.o src/chrono_io.cc clang++ -std=c++11 -rdynamic -Lsrc -o src/chrono_io src/chrono_io.o -lboost_chrono -lboost_system -lpthread -L/usr/lib/x86_64-linux-gnu LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu ./src/chrono_io terminate called after throwing an instance of 'std::bad_cast' what(): std::bad_cast Aborted (core dumped) Makefile:13: recipe for target 'test' failed make: *** [test] Error 13
Here is the code around boost/include/boost/chrono/io_v1/chrono_io.hpp:210
template <class CharT, class Traits, class Rep, class Period> std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d) { typedef duration_punct<CharT> Facet; std::locale loc = os.getloc(); if (!std::has_facet<Facet>(loc)) os.imbue(std::locale(loc, new Facet)); const Facet& f = std::use_facet<Facet>(os.getloc()); //<<<<<<< line210, throw here return os << d.count() << ' ' << f.template name<Period>(d.count()); }
maybe we could avoid calling os.getloc() again at line 210 to avoid the race?
Attachments (1)
Change History (12)
by , 7 years ago
Attachment: | chrono_io_bad_cast.tgz added |
---|
comment:1 by , 7 years ago
comment:2 by , 7 years ago
The following avoids the race, but breaks other functionalities
template <class CharT, class Traits, class Rep, class Period> std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d) { typedef duration_punct<CharT> Facet; Facet f; return os << d.count() << ' ' << f.template name<Period>(d.count()); }
I tried with version 2 and I was not able to reproduce the issue. Please let me know.
comment:3 by , 7 years ago
For what I understand std::ios_base::imbue is not thread-safe.
Version 1 is bugged as it does an imbue if the facet is not present, raising possible undefined behavior.
Version 2 duration operator<< doesn't imbues the facet by itself. If not present, it uses duration_put.
comment:4 by , 7 years ago
Component: | None → chrono |
---|---|
Owner: | set to |
Status: | new → assigned |
comment:5 by , 7 years ago
Hi Vincente, thank you for answering so quickly.
I tried with version 2 and I was not able to reproduce the issue. Please let me know.
same here.
For what I understand std::ios_base::imbue is not thread-safe. Version 1 is bugged as it does an imbue if the facet is not present, raising possible undefined behavior. Version 2 duration operator<< doesn't imbues the facet by itself. If not present, it uses duration_put.
would it be possible to fix v1? Maybe with something like this (untested):
template <class CharT, class Traits, class Rep, class Period> std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d) { typedef duration_punct<CharT> Facet; os << d.count() << ' '; try { os << std::use_facet<Facet>(os.getloc()).template name<Period>(d.count()); } catch (std::bad_cast) { // os locale does not have the facet os << Facet().template name<Period>(d.count()); } return os; }
follow-up: 7 comment:6 by , 7 years ago
Have you tried this? This is in some way what version 2 does.
comment:7 by , 7 years ago
Replying to viboes:
Have you tried this? This is in some way what version 2 does.
No (as the "untested" suggested), and sadly I'm by no way an iostream/locale expert.
comment:11 by , 5 years ago
Summary: | chrono IO may throw bad_cast → chrono IO V1 may throw bad_cast |
---|
Hi,
I didn't through to multi-threading while developing this code :(
Could you try to comment the line? Could you also try version v2 by defining before including any boost/chrono/... file