This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Shouldn't ios_base::Init::Init() be reentrant?
- To: libstdc++ at gcc dot gnu dot org
- Subject: Shouldn't ios_base::Init::Init() be reentrant?
- From: Carlo Wood <carlo at alinoe dot com>
- Date: Mon, 21 May 2001 03:30:23 +0200
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>