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

Reply via email to