eastig created this revision. eastig added a subscriber: cfe-commits. When an executable (e.g. for bare metal environment) is statically linked with libc++ the following code might not work as expected:
``` #include <iostream> class Foo { public: Foo(int n) { std::cout << "Hello World - " << n << std::endl; } }; Foo f(5); int main() { return 0; } ``` The program hangs or crashes because 'std::cout' is not initialized before 'Foo f(5)'. The standard says: > 27.3 Standard iostream objects > Header <iostream> synopsis > (2) ... The objects are constructed, and the associations are established at > some time prior to or during first > time an object of class basic_ios<charT,traits>::Init is constructed, and in > any case before the body of main > begins execution [264]. The objects are not destroyed during program > execution [265]. > And footnote 265 says: > Constructors and destructors for static objects can access these objects to > read input from stdin or write output > to stdout or stderr. The similar issue was raised more than year ago. A patch was proposed but not committed. See discussion of it here: [[ http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130304/075363.html | initial patch ]] [[ http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130325/077130.html | review, suggestion for #ifdef ]] The proposed patch caused initialization/deinitialization of the std streams as many times as the static Init objects were created. The current patch fixes this issue. A number of uses is counted by means of __shared_count. It is used instead of a 'int' variable because it is thread-safe. The standard allows multi-threaded initialization of static objects from different compilation modules. http://reviews.llvm.org/D12689 Files: include/ios src/iostream.cpp
Index: src/iostream.cpp =================================================================== --- src/iostream.cpp +++ src/iostream.cpp @@ -10,6 +10,8 @@ #include "__std_stream" #include "string" #include "new" +#include "cassert" +#include "memory" _LIBCPP_BEGIN_NAMESPACE_STD @@ -41,9 +43,7 @@ _ALIGNAS_TYPE (ostream) _LIBCPP_FUNC_VIS char clog[sizeof(ostream)]; _ALIGNAS_TYPE (wostream) _LIBCPP_FUNC_VIS char wclog[sizeof(wostream)]; -ios_base::Init __start_std_streams; - -ios_base::Init::Init() +static void init_std_streams() { #ifndef _LIBCPP_HAS_NO_STDIN istream* cin_ptr = ::new(cin) istream(::new(__cin) __stdinbuf <char>(stdin, &mb_cin)); @@ -70,7 +70,7 @@ #endif } -ios_base::Init::~Init() +static void fini_std_streams() { #ifndef _LIBCPP_HAS_NO_STDOUT ostream* cout_ptr = reinterpret_cast<ostream*>(cout); @@ -85,4 +85,51 @@ wclog_ptr->flush(); } +/// __Init is a utility class that counts the number of ios_base::Init +/// objects. It automatically deinitializes the std streams when the +/// last ios_base::Init object is destroyed. +class __Init: public __shared_count { +private: + void __on_zero_shared() _NOEXCEPT { + fini_std_streams(); + } +public: + __Init(long count = 0): __shared_count(count) { + init_std_streams(); + } +}; + +_ALIGNAS_TYPE (__Init) _LIBCPP_FUNC_VIS static char __init_storage[sizeof(__Init)]; + +__shared_count *ios_base::Init::UseCount = nullptr; + +ios_base::Init::Init() +{ + //local static object is initialized only once + struct __S { + __S() { + Init::UseCount = ::new(__init_storage) __Init(-1); + } + }; + static __S __s; + UseCount->__add_shared(); +} + +ios_base::Init::~Init() +{ + assert(UseCount); + UseCount->__release_shared(); +} + +#ifdef __APPLE__ +// Apple linker has a guarantee for an initialization order: +// if A links against B, B's initializer will be run before A's. +// I.e. if you link to libc++.dylib, then cout et al. are guaranteed +// to be constructed before your initializers run. This way, initialization +// of __start_std_streams might remain here in module initializer +// Without Apple linker, the following definition must be in header <ios>, +// so its initialization will be explicitly executed before any stream usage. +ios_base::Init __start_std_streams; +#endif + _LIBCPP_END_NAMESPACE_STD Index: include/ios =================================================================== --- include/ios +++ include/ios @@ -423,13 +423,30 @@ virtual ~failure() throw(); }; +class __shared_count; + class _LIBCPP_TYPE_VIS ios_base::Init { +private: + static __shared_count *UseCount; public: Init(); ~Init(); }; +#ifndef __APPLE__ +// Apple linker has a guarantee for an initialization order: +// if A links against B, B's initializer will be run before A's. +// I.e. if you link to libc++.dylib, then cout et al. are guaranteed +// to be constructed before your initializers run. This way, definition +// of __start_std_streams might remain in src/iostream.cpp, and linker arranges +// its initialization before any stream usage. +// Without Apple linker, that definition must be here, +// so its initialization will be explicitly executed before any stream usage. +// For construction of cout, cin, cerr etc. +static ios_base::Init __start_std_streams; +#endif + // fmtflags inline _LIBCPP_INLINE_VISIBILITY
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits