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 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 (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(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 , +// so its initialization will be explicitly executed before any stream usage. +ios_base::Init __start_std_streams; +#endif + _LIBCPP_END_NAMESPACE_STD Index: test/std/input.output/iostream.objects/narrow.stream.objects/cerr_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/narrow.stream.objects/cerr_init.pass.cpp +++ test/std/input.output/iostream.objects/narrow.stream.objects/cerr_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::cerr' is the same in a static object constructor +// and in the main function. +// It dumps std::cerr memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::cerr it must be the same. + +struct A { + char *cerr_mem_dump; + + A(): cerr_mem_dump(new char[sizeof(std::cerr)]) { + std::memcpy(cerr_mem_dump, (char *)&std::cerr, sizeof(std::cerr)); + } + ~A() { + delete [] cerr_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.cerr_mem_dump, (const char *)&std::cerr, sizeof(std::cerr)) == 0); +} + Index: test/std/input.output/iostream.objects/narrow.stream.objects/cin_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/narrow.stream.objects/cin_init.pass.cpp +++ test/std/input.output/iostream.objects/narrow.stream.objects/cin_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::cin' is the same in a static object constructor +// and in the main function. +// It dumps std::cin memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::cin it must be the same. + +struct A { + char *cin_mem_dump; + + A(): cin_mem_dump(new char[sizeof(std::cin)]) { + std::memcpy(cin_mem_dump, (char *)&std::cin, sizeof(std::cin)); + } + ~A() { + delete [] cin_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.cin_mem_dump, (char *)&std::cin, sizeof(std::cin)) == 0); +} + Index: test/std/input.output/iostream.objects/narrow.stream.objects/clog_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/narrow.stream.objects/clog_init.pass.cpp +++ test/std/input.output/iostream.objects/narrow.stream.objects/clog_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::clog' is the same in a static object constructor +// and in the main function. +// It dumps std::clog memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::clog it must be the same. + +struct A { + char *clog_mem_dump; + + A(): clog_mem_dump(new char[sizeof(std::clog)]) { + std::memcpy(clog_mem_dump, (char *)&std::clog, sizeof(std::clog)); + } + ~A() { + delete [] clog_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.clog_mem_dump, (char *)&std::clog, sizeof(std::clog)) == 0); +} + Index: test/std/input.output/iostream.objects/narrow.stream.objects/cout_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/narrow.stream.objects/cout_init.pass.cpp +++ test/std/input.output/iostream.objects/narrow.stream.objects/cout_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::cout' is the same in a static object constructor +// and in the main function. +// It dumps std::cout memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::cout it must be the same. + +struct A { + char *cout_mem_dump; + + A(): cout_mem_dump(new char[sizeof(std::cout)]) { + std::memcpy(cout_mem_dump, (char *)&std::cout, sizeof(std::cout)); + } + ~A() { + delete [] cout_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.cout_mem_dump, (char *)&std::cout, sizeof(std::cout)) == 0); +} + Index: test/std/input.output/iostream.objects/wide.stream.objects/wcerr_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/wide.stream.objects/wcerr_init.pass.cpp +++ test/std/input.output/iostream.objects/wide.stream.objects/wcerr_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::wcerr' is the same in a static object constructor +// and in the main function. +// It dumps std::wcerr memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::wcerr it must be the same. + +struct A { + char *wcerr_mem_dump; + + A(): wcerr_mem_dump(new char[sizeof(std::wcerr)]) { + std::memcpy(wcerr_mem_dump, (char *)&std::wcerr, sizeof(std::wcerr)); + } + ~A() { + delete [] wcerr_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.wcerr_mem_dump, (const char *)&std::wcerr, sizeof(std::wcerr)) == 0); +} + Index: test/std/input.output/iostream.objects/wide.stream.objects/wcin_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/wide.stream.objects/wcin_init.pass.cpp +++ test/std/input.output/iostream.objects/wide.stream.objects/wcin_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::wcin' is the same in a static object constructor +// and in the main function. +// It dumps std::wcin memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::wcin it must be the same. + +struct A { + char *wcin_mem_dump; + + A(): wcin_mem_dump(new char[sizeof(std::wcin)]) { + std::memcpy(wcin_mem_dump, (char *)&std::wcin, sizeof(std::wcin)); + } + ~A() { + delete [] wcin_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.wcin_mem_dump, (char *)&std::wcin, sizeof(std::wcin)) == 0); +} + Index: test/std/input.output/iostream.objects/wide.stream.objects/wclog_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/wide.stream.objects/wclog_init.pass.cpp +++ test/std/input.output/iostream.objects/wide.stream.objects/wclog_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::wclog' is the same in a static object constructor +// and in the main function. +// It dumps std::wclog memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::wclog it must be the same. + +struct A { + char *wclog_mem_dump; + + A(): wclog_mem_dump(new char[sizeof(std::wclog)]) { + std::memcpy(wclog_mem_dump, (char *)&std::wclog, sizeof(std::wclog)); + } + ~A() { + delete [] wclog_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.wclog_mem_dump, (char *)&std::wclog, sizeof(std::wclog)) == 0); +} + Index: test/std/input.output/iostream.objects/wide.stream.objects/wcout_init.pass.cpp =================================================================== --- test/std/input.output/iostream.objects/wide.stream.objects/wcout_init.pass.cpp +++ test/std/input.output/iostream.objects/wide.stream.objects/wcout_init.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// The test checks that 'std::wcout' is the same in a static object constructor +// and in the main function. +// It dumps std::wcout memory in the static object constructor and compares it +// with the memory in the main function. +// Assumption: if there are no uses of std::wcout it must be the same. + +struct A { + char *wcout_mem_dump; + + A(): wcout_mem_dump(new char[sizeof(std::wcout)]) { + std::memcpy(wcout_mem_dump, (char *)&std::wcout, sizeof(std::wcout)); + } + ~A() { + delete [] wcout_mem_dump; + } +}; + +static A a; + +int main() +{ + assert(memcmp(a.wcout_mem_dump, (char *)&std::wcout, sizeof(std::wcout)) == 0); +} +