segfault in juce::Atomic<int>::operator++()


#1

I'm trying to integrate Juce 3.0.0-39-g488fe56 into an existing C++ proof-of-concept project that was never completed by a previous developer.

I'm getting a segfault deep in Juce when I try to use a new class I wrote that inherits from juce::ApplicationProperties.  I don't understand the Juce code which segfaults, but I suspect it could be due to this being called from a static object before main() has run and before Juce has been initialized.

I've read in the past some posts on the forum about using static objects, and I understand the problems and objections.  But with a large pre-existing project where I don't have a choice without having to re-write how the code works, is there a call I can make so Juce is fully initialized?  Or is there another cause to this problem?

Valgrind prints out the following:


==00:00:00:07.151 23732== Invalid read of size 4
==00:00:00:07.151 23732==    at 0x6E6E27: juce::Atomic<int>::operator++() (/projects/WorkOrder/JuceLibraryCode/modules/juce_graphics/../juce_core/memory/juce_Atomic.h:327)
==00:00:00:07.151 23732==    by 0x6E13B0: juce::StringHolder::retain(juce::CharPointer_UTF8) (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/text/juce_String.cpp:168)
==00:00:00:07.151 23732==    by 0x6A213E: juce::String::operator=(juce::String const&) (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/text/juce_String.cpp:264)
==00:00:00:07.151 23732==    by 0x6B13F0: juce::XmlDocument::parseDocumentElement(juce::CharPointer_UTF8, bool) (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp:213)
==00:00:00:07.151 23732==    by 0x6B0FDD: juce::XmlDocument::getDocumentElement(bool) (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp:142)
==00:00:00:07.151 23732==    by 0x704098: juce::PropertiesFile::loadAsXml() (/projects/WorkOrder/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp:182)
==00:00:00:07.151 23732==    by 0x703B60: juce::PropertiesFile::reload() (/projects/WorkOrder/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp:129)
==00:00:00:07.151 23732==    by 0x703A4F: juce::PropertiesFile::PropertiesFile(juce::PropertiesFile::Options const&) (/projects/WorkOrder/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp:119)
==00:00:00:07.151 23732==    by 0x703389: juce::ApplicationProperties::openFiles() (/projects/WorkOrder/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp:54)
==00:00:00:07.151 23732==    by 0x70349B: juce::ApplicationProperties::getUserSettings() (/projects/WorkOrder/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp:70)
==00:00:00:07.151 23732==    by 0x65F6E7: WO::Config::getString(juce::String const&, juce::String const&) (/projects/WorkOrder/src-config/WOConfig.cpp:55)
==00:00:00:07.151 23732==    by 0x65F619: WO::Config::operator[](juce::String const&) (/projects/WorkOrder/src-config/WOConfig.cpp:48)
==00:00:00:07.151 23732==    by 0x6564AF: WO::DatabaseAccess::DatabaseAccess(std::string const&) (/projects/WorkOrder/src-base/DatabaseAccess.cpp:19)
==00:00:00:07.151 23732==    by 0x65E6E9: __static_initialization_and_destruction_0(int, int) (/projects/WorkOrder/src-base/StaticObjects.cpp:71)
==00:00:00:07.151 23732==    by 0x65E75C: _GLOBAL__sub_I__Z8initJucev (/projects/WorkOrder/src-base/StaticObjects.cpp:72)
==00:00:00:07.151 23732==    by 0x93801C: __libc_csu_init (in /home/stephane/projects/WorkOrder/build/src-wo/wo)
==00:00:00:07.151 23732==    by 0x697BE34: (below main) (/build/buildd/eglibc-2.17/csu/libc-start.c:219)
==00:00:00:07.151 23732==  Address 0xfffffffffffffff0 is not stack'd, malloc'd or (recently) free'd

In gdb, I see this:


325   #elif JUCE_ATOMICS_GCC
326     return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1)
327                               : (Type) __sync_add_and_fetch ((int64_t*) &value, 1);
328   #endif

(gdb) print value
Cannot access memory at address 0xfffffffffffffff0

...with the error on line 327.


#2

Yeah, you've got code that's running before the String class has had chance to initialise String::empty. There's no good answer to this apart from not doing that kind of complex work in static constructors. There's really no excuse for it!

Surely you could just move the file-static object to be a function-static, so it only gets created when it's first needed?


#3

Another indication this is due to making calls to Juce from objects constructed statically:

Stack frame #3 is:  lastError = String::empty;
Stack frame #1 is:  if b != emptyString then ++refcount
Stack frame #0 is the segfault in juce::Atomic operator++.

So juce::XmlDocument::parseDocumentElement() element sets an error string to String::empty, and even though juce::StringHolder tries to prevent reference counting the empty string (am I reading this code correctly?) it looks like this takes place anyway.  Probably because emptyString doesn't exist yet?

Jules, I'm not asking for a fix to this since I understand your objections to using static objects.  But can you think of a workaround that I could use, possibly to try and "force" Juce to fully initialize itself?  All of this project's static objects are in 1 .cpp file, so I can guarantee the order in which they get initialized.  I just need to know what call to make into Juce.

Thanks for any pointers!


#4

Unfortunately it is needed during static initialization.  The application reads a large amount of data from a database and stores them in static objects.  What I was trying to store in a configuration file and accessing it via ApplicationProperties is all of the database connection information, such as IP address, db name, schema name, etc.  At the moment all that information is hard-coded into the database class.

Moving all of the static initialization for this application to happen after main() starts is a big deal.  Isn't there something I can call to ensure Juce is initialized?


#5

..actually, try what I've just checked-in. Although it's very unwise to use static objects like this, I can see that people will always do it, so I've just removed a few of the core uses of String::empty. It may be enough to get your code working, and may save me having to answer the same question again in future!


#6

Notice how the C++ standard library doesn't suffer from this problem.


#7

Thanks, I got a bit further.  Now a similar segfault with the empty string is happening from PropertySet::getValue().  I'll take a look at your last commit to see if I can figure out what to change and submit a patch.

(Is there a call I can make to ensure juce::String is fully initialized?)

Thanks again, Jules.

The new crash is here:

==00:00:00:07.654 6698== Invalid read of size 4
==00:00:00:07.655 6698==    at 0x704F0B: juce::Atomic<int>::operator++() (/projects/WorkOrder/JuceLibraryCode/modules/juce_data_structures/../juce_events/../juce_core/memory/juce_Atomic.h:327)
==00:00:00:07.655 6698==    by 0x6FF438: juce::StringHolder::retain(juce::CharPointer_UTF8) (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/text/juce_String.cpp:168)
==00:00:00:07.655 6698==    by 0x6BFEC5: juce::String::String(juce::String const&) (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/text/juce_String.cpp:254)
==00:00:00:07.655 6698==    by 0x6A7231: juce::PropertySet::getValue(juce::StringRef, juce::String const&) const (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp:78)
==00:00:00:07.655 6698==    by 0x6A721C: juce::PropertySet::getValue(juce::StringRef, juce::String const&) const (/projects/WorkOrder/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp:78)
==00:00:00:07.655 6698==    by 0x67D4FE: WO::Config::getString(juce::String const&, juce::String const&) (/projects/WorkOrder/src-config/WOConfig.cpp:55)
==00:00:00:07.655 6698==    by 0x67D419: WO::Config::operator[](juce::String const&) (/projects/WorkOrder/src-config/WOConfig.cpp:48)
==00:00:00:07.655 6698==    by 0x6742AF: WO::DatabaseAccess::DatabaseAccess(std::string const&) (/projects/WorkOrder/src-base/DatabaseAccess.cpp:19)
==00:00:00:07.655 6698==    by 0x67C4E9: __static_initialization_and_destruction_0(int, int) (/projects/WorkOrder/src-base/StaticObjects.cpp:71)
==00:00:00:07.655 6698==    by 0x67C55C: _GLOBAL__sub_I__Z8initJucev (/projects/WorkOrder/src-base/StaticObjects.cpp:72)
==00:00:00:07.655 6698==    by 0x96B7BC: __libc_csu_init (in /home/stephane/projects/WorkOrder/build/src-wo/wo)
==00:00:00:07.655 6698==    by 0x6BD9E34: (below main) (/build/buildd/eglibc-2.17/csu/libc-start.c:219)
==00:00:00:07.655 6698==  Address 0xfffffffffffffff0 is not stack'd, malloc'd or (recently) free'd



#8

I've made a few more changes now, so see it that helps.


#9

Bingo!  Thanks, Jules.  I'm now on v3.0.0-41-g2edec00 and have gotten past the segfault problem completely.

Found another place in my own code where I was using String::empty as a parameter during static object construction.  Once I changed that to "" things are looking good.  I'm now debugging through some PostgreSQL code since the new database layout is slightly different than the prototype app, so a completely different issue unrelated to Juce.


#10

Surely you could just move the file-static object to be a function-static, so it only gets created when it's first needed?

Just as a matter of personal curiosity, and not to start any kind of flame war about it or anything (I know TheVinn is very sensitive to the subject), why is not JUCE doing that in the first place?

Another thought on the subject: for those classes (String, File, etc) that use static members, why not adding a jassert in their constructor that checks that JUCE is properly initialised when they are being constructed? That would make clear without doubts what's going on. Having to think about it because you end having problems about some obscure code about atomics is quite misleading instead.


#11

Way back years ago, there were some initialisation calls that had to be made before any juce library code could be called, and that prevented anyone doing silly stuff like having complex code running in static constructors. When I relaxed the initialisation requirements, I guess I should have updated this stuff too, but just didn't notice that it was a problem.

An assertion might be possible.. not 100% sure, and I think it'd be a bit fiddly to create something that'd detect that correctly, but I'll have a look if I get chance.


#12

Sorry to bring up this old thread, but right now I'm struggling with this as well. I have an application built in Qt and want to use some functionality from an external static library that uses JUCE. So, I build the JUCE project into a static library and link the Qt executable project against that.

In a seperate cpp file in the Qt project I call a function from the static library. Afaik I don't have to initialise juce because I'm only using juce_Core, right? However, I get a crash in the function inline Type Atomic<Type>::operator--() noexcept on the following:

#elif JUCE_ATOMICS_GCC
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1)
: (Type) __sync_add_and_fetch ((int64_t*) &value, -1);
#endif

Stacktrace:

0    juce::Atomic<int>::operator--    juce_Atomic.h    341    0x437ced    
1    juce::StringHolder::release    juce_String.cpp    174    0x4ef7d4    
2    juce::StringHolder::release    juce_String.cpp    180    0x4ef826    
3    juce::String::~String    juce_String.cpp    249    0x4ae7a3    
...  yadayada my functions
10    main    main.cpp    24    0x40c529    
 

Valgrind also says something related to this:

Invalid read of size 4 in Test::doSomething(void const*, int) in App/Test.cpp:15 Address 0xfffffffffffffff0 is not stack'd, malloc'd or (recently) free'd 1: juce::Atomic<int>::operator--() in /home_ssd/francois/Repositories/juce/modules/juce_core/memory/juce_Atomic.h:341 2: juce::StringHolder::release(juce::StringHolder*) in /home_ssd/francois/Repositories/juce/modules/juce_core/text/juce_String.cpp:174 3: juce::StringHolder::release(juce::CharPointer_UTF8) in /home_ssd/francois/Repositories/juce/modules/juce_core/text/juce_String.cpp:180 4: juce::String::~String() in /home_ssd/francois/Repositories/juce/modules/juce_core/text/juce_String.cpp:249

I'm a bit clueless here, do you have an idea?

(this is with latest juce git)


#13

The String that you're trying to delete is a dangling or invalid pointer.