Building without RTTI support causes "make check" to hang (recursive call to pthread_once?)

536 views
Skip to first unread message

Oliver Jowett

unread,
Aug 18, 2009, 11:26:48 PM8/18/09
to prot...@googlegroups.com
I'm trying to build 2.2.0 without RTTI support and running into some
obscure problems. The environment is:

$ g++ -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu
4.3.3-5ubuntu4' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs
--enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3
--enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc
--enable-mpfr --with-tune=generic --enable-checking=release
--build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.3 (Ubuntu 4.3.3-5ubuntu4)

Results so far:

The default configuration (just ./configure) passes make check.

With CXXFLAGS="-g -DNDEBUG -DGTEST_HAS_RTTI=0 -DGOOGLE_PROTOBUF_NO_RTTI
-fno-rtti" PROTOBUF_OPT_FLAG="-O2": make check hangs on the
CustomOptions.OptionLocations test. The stack trace looks like this:

> (gdb) where
> #0 0x00002b85e53005cb in pthread_once () from /lib/libpthread.so.0
> #1 0x0000000000426431 in google::protobuf::GoogleOnceInit (once=0xa7b148, init_func=0x726bfd <protobuf_unittest::protobuf_AssignDesc_google_2fprotobuf_2funittest_5fcustom_5foptions_2eproto()>)
> at ./google/protobuf/stubs/once.h:115
> #2 0x000000000071c3c0 in protobuf_AssignDescriptorsOnce () at google/protobuf/unittest_custom_options.pb.cc:342
> #3 0x000000000071c4c9 in protobuf_unittest::ComplexOpt6::GetMetadata (this=0x262e320) at google/protobuf/unittest_custom_options.pb.cc:3796
> #4 0x00002b85e3ffb67a in google::protobuf::internal::ReflectionOps::Merge (from=@0x262e320, to=0x268f0e0) at ./google/protobuf/message.h:311
> #5 0x000000000071dc4a in protobuf_unittest::ComplexOpt6::MergeFrom (this=0x268f0e0, from=@0x262e320) at google/protobuf/unittest_custom_options.pb.cc:3753
> #6 0x00002b85e3fa844e in google::protobuf::internal::ExtensionSet::MergeFrom (this=0x268f048, other=<value optimized out>) at google/protobuf/extension_set.cc:644
> #7 0x00002b85e3fdf191 in google::protobuf::MessageOptions::MergeFrom (this=0x268f040, from=@0x268a530) at google/protobuf/descriptor.pb.cc:4732
> #8 0x00002b85e3fcebf3 in google::protobuf::DescriptorBuilder::AllocateOptionsImpl<google::protobuf::Descriptor> (this=0x7fffc6d68730, name_scope=@0x268ef40, element_name=@0x268ef40,
> orig_options=@0x268a530, descriptor=0x2687820) at google/protobuf/descriptor.cc:2648
> #9 0x00002b85e3fc6fcb in google::protobuf::DescriptorBuilder::BuildMessage (this=0x7fffc6d68730, proto=@0x268a370, parent=0x0, result=0x2687820) at google/protobuf/descriptor.cc:2625
> #10 0x00002b85e3fc7a86 in google::protobuf::DescriptorBuilder::BuildFile (this=0x7fffc6d68730, proto=@0x7fffc6d687b0) at google/protobuf/descriptor.cc:2814
> #11 0x00002b85e3fc8c5f in google::protobuf::DescriptorPool::BuildFileFromDatabase (this=0x25b6c90, proto=@0x7fffc6d687b0) at google/protobuf/descriptor.cc:2197
> #12 0x00002b85e3fc9c53 in google::protobuf::DescriptorPool::TryFindFileInFallbackDatabase (this=0x25b6c90, name=@0x7fffc6d68ac0) at google/protobuf/descriptor.cc:1230
> #13 0x00002b85e3fc9d40 in google::protobuf::DescriptorPool::FindFileByName (this=0x25b6c90, name=@0x7fffc6d68ac0) at google/protobuf/descriptor.cc:875
> #14 0x0000000000726c3c in protobuf_unittest::protobuf_AssignDesc_google_2fprotobuf_2funittest_5fcustom_5foptions_2eproto () at google/protobuf/unittest_custom_options.pb.cc:79
> #15 0x00002b85e53005d3 in pthread_once () from /lib/libpthread.so.0
> #16 0x0000000000426431 in google::protobuf::GoogleOnceInit (once=0xa7b148, init_func=0x726bfd <protobuf_unittest::protobuf_AssignDesc_google_2fprotobuf_2funittest_5fcustom_5foptions_2eproto()>)
> at ./google/protobuf/stubs/once.h:115
> #17 0x000000000071c3c0 in protobuf_AssignDescriptorsOnce () at google/protobuf/unittest_custom_options.pb.cc:342
> #18 0x000000000071ca01 in protobuf_unittest::TestMessageWithCustomOptions::descriptor () at google/protobuf/unittest_custom_options.pb.cc:766
> #19 0x0000000000479d88 in google::protobuf::descriptor_unittest::CustomOptions_OptionLocations_Test::TestBody (this=0x262eab0) at google/protobuf/descriptor_unittest.cc:1956
> #20 0x00002b85e44ec225 in testing::Test::Run (this=0x262eab0) at src/gtest.cc:2135
> #21 0x00002b85e44f14de in testing::internal::TestInfoImpl::Run (this=0x260e980) at src/gtest.cc:2355
> #22 0x00002b85e44f9716 in testing::internal::TestInfoImpl::RunTest (test_info=0x260e960) at ./src/gtest-internal-inl.h:496
> #23 0x00002b85e44f22be in testing::internal::Vector<testing::TestInfo*>::ForEach<void (*)(testing::TestInfo*)> (this=0x260eb40,
> functor=0x2b85e44f96f9 <testing::internal::TestInfoImpl::RunTest(testing::TestInfo*)>) at ./src/gtest-internal-inl.h:344
> #24 0x00002b85e44f13c9 in testing::internal::TestCase::Run (this=0x260eaf0) at src/gtest.cc:2455
> #25 0x00002b85e44f96f7 in testing::internal::TestCase::RunTestCase (test_case=0x260eaf0) at ./include/gtest/gtest.h:703
> #26 0x00002b85e44f1f56 in testing::internal::Vector<testing::internal::TestCase*>::ForEach<void (*)(testing::internal::TestCase*)> (this=0x25c8a20,
> functor=0x2b85e44f96e2 <testing::internal::TestCase::RunTestCase(testing::internal::TestCase*)>) at ./src/gtest-internal-inl.h:344
> #27 0x00002b85e44f1214 in testing::internal::UnitTestImpl::RunAllTests (this=0x25c89a0) at src/gtest.cc:3814
> #28 0x00002b85e44f132d in testing::UnitTest::Run (this=0x2b85e4714fd0) at src/gtest.cc:3544
> #29 0x00002b85e4716a5d in main (argc=1, argv=0x7fffc6d69148) at src/gtest_main.cc:38
> #30 0x00002b85e552f5a6 in __libc_start_main () from /lib/libc.so.6
> #31 0x0000000000415f69 in _start () at ../sysdeps/x86_64/elf/start.S:113

Running just that test in isolation (./protobuf-test
--gtest_filter=CustomOptions.*) fails in the same way.

Running just the GoogleOnceInit tests (./protobuf-test
--gtest_filter=OnceInitTest.*) passes.

Configuring with CXXFLAGS="-g -DNDEBUG -DGOOGLE_PROTOBUF_NO_RTTI"
PROTOBUF_OPT_FLAG="-O2" fails in the same way.

The difference in behaviour is in this bit of generated code:

> void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) {
> GOOGLE_CHECK_NE(&from, this);
> const MessageOptions* source =
> ::google::protobuf::internal::dynamic_cast_if_available<const MessageOptions*>(
> &from);
> if (source == NULL) {
> ::google::protobuf::internal::ReflectionOps::Merge(from, this);
> } else {
> MergeFrom(*source);
> }
> }

When GOOGLE_PROTOBUF_NO_RTTI is defined, dynamic_cast_if_available will
always return NULL and a different code path runs.

I see some search results that say that pthread_once() can deadlock if
called recursively with the same argument, so presumably the root issue
is that GoogleOnceInit doesn't avoid doing that.

-O

Oliver Jowett

unread,
Aug 18, 2009, 11:42:34 PM8/18/09
to prot...@googlegroups.com
Oliver Jowett wrote:

>> #4 0x00002b85e3ffb67a in google::protobuf::internal::ReflectionOps::Merge (from=@0x262e320, to=0x268f0e0) at ./google/protobuf/message.h:311
>> #5 0x000000000071dc4a in protobuf_unittest::ComplexOpt6::MergeFrom (this=0x268f0e0, from=@0x262e320) at google/protobuf/unittest_custom_options.pb.cc:3753
>> #6 0x00002b85e3fa844e in google::protobuf::internal::ExtensionSet::MergeFrom (this=0x268f048, other=<value optimized out>) at google/protobuf/extension_set.cc:644
>> #7 0x00002b85e3fdf191 in google::protobuf::MessageOptions::MergeFrom (this=0x268f040, from=@0x268a530) at google/protobuf/descriptor.pb.cc:4732

> The difference in behaviour is in this bit of generated code:


>
>> void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) {

Sorry, wrong bit of code. The MessageOptions::MergeFrom in the
stacktrace is actually the "const MessageOptions &from" variant. It
looks like it's actually ComplexOpt6::MergeFrom that differs in
behaviour with RTTI off.

-O

Kenton Varda

unread,
Aug 19, 2009, 3:45:49 PM8/19/09
to Oliver Jowett, prot...@googlegroups.com
Sigh.  More bugs in descriptor bootstrapping, eh?

The problem is that without RTTI, MergeFrom(const Message& other) cannot tell if "other" is actually the exact same class as "this", so it has to fall back to reflection...  but reflection does not work if descriptors aren't available...  and we're in the middle of building said descriptors at the time.  So you get a deadlock, because descriptor initialization cannot complete until descriptor initialization completes.

The solution is to avoid calling the generic version of MergeFrom anywhere in descriptor.cc.  I'll put it on my TODO to fix this.  Note that this only matters if you use custom options -- if not you should be safe.

Vanuan

unread,
Feb 8, 2012, 6:39:40 PM2/8/12
to prot...@googlegroups.com, Oliver Jowett
Hi, Kenton!
I also use -fno-rtti and have the same problem. Did you manage to fix it?

Jessica Calvert

unread,
Mar 29, 2012, 4:56:14 PM3/29/12
to prot...@googlegroups.com, Oliver Jowett
Hello Kenton,

Any progress with this issue? I am also compiling without RTTI support and using custom options. I am running into the hang described here and trying to figure out a way around it.

-Jessica
Reply all
Reply to author
Forward
0 new messages