This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Shouldn't ios_base::Init::Init() be reentrant?


Guys,

I *really* tried - but with the current implementation of libstdc++ I can't
manage to get working what I want to get working.  Therefore I'd like to
ask for a feature - and if that not is possible - claim that the Standard
demands the following:

27.4.2.1.6 Class ios_base::Init			27 Input/output library

	Init();
	
Effects: Constructs an object of class Init. If init_cnt is zero, the function stores
the value one in init_cnt, then constructs and initializes the objects cin, cout, cerr,
clog (27.3.1), wcin, wcout, wcerr, and wclog (27.3.2).  In any case, the function then
                                                                                  ^^^^
adds one to the value stored in init_cnt.


In other words: this function should FIRST initialize cin, cout, cerr, etc and
THEN increment init_cnt.


Further more (and more literally what I need) in 27.4.6.1:

 1 The class Init describes an object whose construction ensures the construction of the
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
   eight objects declared in <iostream> (27.3) that associate file stream buffers with the
   standard C streams provided for by the functions declared in <cstdio> (27.8.2).

---

Now you might wonder how on earth I manage to have problems with the current
implementation I am sure ;).  The problem is that I need an initialized 'cout' and 'cerr'
etc. inside an overridden operator new.

In the current implementation creation of ios_base::Init does the following:

  ios_base::Init::Init()
  {
    if (++_S_ios_base_init == 1)
    {
	// Standard streams default to synced with "C" operations.
	ios_base::Init::_S_synced_with_stdio = true;
	_S_ios_create(ios_base::Init::_S_synced_with_stdio);
    }
  }

where

  void
  ios_base::Init::_S_ios_create(bool __sync)
  {
    int __bufsize = __sync ? 0 : static_cast<int>(BUFSIZ);
    // NB: The file std_iostream.h creates the four standard files
    // with NULL buffers. At this point, we swap out the dummy NULL
    // buffers with the real deal.
    new (&cout) ostream(new filebuf(stdout, ios_base::out, __bufsize));
    new (&cin) istream(new filebuf(stdin, ios_base::in, 1));
    new (&cerr) ostream(new filebuf(stderr, ios_base::out, __bufsize));
    new (&clog) ostream(cerr.rdbuf());
    cin.tie(&cout);
    cerr.flags(ios_base::unitbuf);
 
#ifdef _GLIBCPP_USE_WCHAR_T
    new (&wcout) wostream( new wfilebuf(stdout, ios_base::out, __bufsize));
    new (&wcin) wistream(new wfilebuf(stdin, ios_base::in, 1));
    new (&wcerr) wostream(new wfilebuf(stderr, ios_base::out, __bufsize));
    new (&wclog) wostream(wcerr.rdbuf());
    wcin.tie(&wcout);
    wcerr.flags(ios_base::unitbuf);
#endif
  }


In other words: creation of Init() first increments _S_ios_base_init and THEN
initializes cout, cerr etc, using a call to operator new.

When I create an object Init() inside operator new, I expect that after that
I can use cout, cerr etc.  But that is clearly not the case at the moment.

In order to solve this problem I need at the very least be able to detect
when we get at the end of _S_ios_create: _S_ios_base_init should be incremented
AFTER the call to _S_ios_create.  But, unless I understand the standard wrongly,
Init::Init() should be reentrant, and simply creating an Init() object (once of
course) inside operator new should work and give me an initialized cout, cerr etc.
objects.

Here is a test case:

--------------------
#include <iostream>

void* operator new(size_t size)
{
  static std::ios_base::Init dummy;
  std::cout << "Calling operator new\n";
  return malloc(size);
}

int main(void)
{
  return 0;
}
--------------------

Program received signal SIGSEGV, Segmentation fault.
0x08049e85 in std::ostream::sentry::sentry(std::ostream&) (this=0xbffff450, __os=@0x804d758)
    at /usr/local/gcc-3.0/include/g++-v3/bits/ostream.tcc:39
39          : _M_ok(__os.good()), _M_os(__os)
Current language:  auto; currently c++
(gdb) bt
#0  0x08049e85 in std::ostream::sentry::sentry(std::ostream&) (this=0xbffff450,
    __os=@0x804d758) at /usr/local/gcc-3.0/include/g++-v3/bits/ostream.tcc:39
#1  0x08049cd6 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (
    __out=@0x804d758, __s=0x804b6e0 "Calling operator new\n")
    at /usr/local/gcc-3.0/include/g++-v3/bits/std_ostream.h:635
#2  0x08049b18 in operator new(unsigned) (size=104) at test.cc:6
#3  0x400517aa in std::ios_base::Init::_S_ios_create(bool) (__sync=true)
    at /usr/src/gcc/gcc-cvs-3.0/libstdc++-v3/include/bits/std_ostream.h:67
#4  0x40054355 in std::ios_base::Init::Init() (this=0xbffff640)
    at /usr/src/gcc/gcc-cvs-3.0/libstdc++-v3/src/ios.cc:166
#5  0x08049c35 in _GLOBAL__I__Znwj ()
    at /usr/local/gcc-3.0/include/g++-v3/bits/localefwd.h:75
#6  0x08049c97 in __do_global_ctors_aux ()
    at /usr/local/gcc-3.0/include/g++-v3/bits/localefwd.h:75
#7  0x080497ba in _init ()
#8  0x4019ff1a in __libc_start_main (main=0x8049b30 <main>, argc=1, ubp_av=0xbffff69c,
    init=0x80497a4 <_init>, fini=0x804b690 <_fini>, rtld_fini=0x4000e274 <_dl_fini>,
    stack_end=0xbffff694) at ../sysdeps/generic/libc-start.c:122

==================================================

The following example would solve my problem,

1) Increment _S_ios_base_init after _S_ios_create instead of before:

  ios_base::Init::Init()
  {
    if (_S_ios_base_init == 0)
    {
	// Standard streams default to synced with "C" operations.
	ios_base::Init::_S_synced_with_stdio = true;
	_S_ios_create(ios_base::Init::_S_synced_with_stdio);
    }
    ++_S_ios_base_init;
  }

2) Assume _S_ios_create can be called from operator new (anywhere during
   the construction of a filebuf and/or an ostream object:

  void
  ios_base::Init::_S_ios_create(bool __sync)
  {
    int __bufsize = __sync ? 0 : static_cast<int>(BUFSIZ);

    // In order to allow reentrant calls to _S_ios_create (from
    // operator new) we first initialize dummy objects.
    fake_ostream tmp_cout, tmp_cerr, tmp_clog;
    fake_istream tmp_cin;
    
    new (&tmp_cout) ostream(new filebuf(stdout, ios_base::out, __bufsize));
    new (&tmp_cin) istream(new filebuf(stdin, ios_base::in, 1));
    new (&tmp_cerr) ostream(new filebuf(stderr, ios_base::out, __bufsize));
    new (&tmp_clog) ostream(cerr.rdbuf());

    if (_S_ios_base_init > 0)	// Was an Init object created from operator new?
    {
      delete tmp_cout.rdbuf();
      delete tmp_cin.rdbuf();
      delete tmp_cerr.rdbuf();
      return;
    }

    // NB: The file std_iostream.h creates the four standard files
    // with NULL buffers.  At this point, we swap out the dummy NULL
    // buffers with the real deal.

    memcpy(&cout, &tmp_cout, sizeof(cout));
    memcpy(&cin, &tmp_cin, sizeof(cin));
    memcpy(&cerr, &tmp_cerr, sizeof(cerr));
    memcpy(&clog, &tmp_clog, sizeof(clog));
    cin.tie(&cout);
    cerr.flags(ios_base::unitbuf);
 
#ifdef _GLIBCPP_USE_WCHAR_T
    fake_wostream tmp_wcout, tmp_wcerr, tmp_wclog;
    fake_wistream tmp_wcin;

    new (&tmp_wcout) wostream( new wfilebuf(stdout, ios_base::out, __bufsize));
    new (&tmp_wcin) wistream(new wfilebuf(stdin, ios_base::in, 1));
    new (&tmp_wcerr) wostream(new wfilebuf(stderr, ios_base::out, __bufsize));
    new (&tmp_wclog) wostream(wcerr.rdbuf());

    if (_S_ios_base_init > 0)
    {
      delete tmp_wcout.rdbuf();
      delete tmp_wcin.rdbuf();
      delete tmp_wcerr.rdbuf();
      return;
    }

    memcpy(&wcout, &tmp_wcout, sizeof(wcout));
    memcpy(&wcin, &tmp_wcin, sizeof(wcin));
    memcpy(&wcerr, &tmp_wcerr, sizeof(wcerr));
    memcpy(&wclog, &tmp_wclog, sizeof(wclog));
    wcin.tie(&wcout);
    wcerr.flags(ios_base::unitbuf);
#endif
  }

-- 
Carlo Wood <carlo@alinoe.com>


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]