StringHolder Crashes


#1

I have been getting some intermittent strange crashes in my program that and I was hoping to get some insight into. Our program is multithreaded, and sometimes I get crashes inside the memcpy in the function

static CharPointerType makeUniqueWithByteSize (const CharPointerType& text, size_t numBytes)
{
    StringHolder* const b = bufferFromText (text);

    if (b->refCount.get() <= 0 && b->allocatedNumBytes >= numBytes)
        return text;

    CharPointerType newText (createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes)));
    memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes);
    release (b);

    return newText;
}

It looks like the text is valid and numBytes looks reasonable. However, b->allocatedNumbytes is wrong. Sometimes is looks like a valid number, but sometimes it is the value 0xdddddddd which would seem to indicate that something has been deleted. The call stack leading up to the crash looks like this.

msvcr90d.dll!memcpy(unsigned char * dst=0x0a6deb50, unsigned char * src=0x0a63ed50, unsigned long count=0xdddddddd) Line 442 Asm
juce_debug.dll!juce::StringHolder::makeUniqueWithByteSize(const juce::CharPointer_UTF8 & text={…}, unsigned int numBytes=0x000000ef) Line 195 + 0x1e bytes C++
juce_debug.dll!juce::String::preallocateBytes(const unsigned int numBytesNeeded=0x000000ee) Line 242 + 0x14 bytes C++
juce_debug.dll!juce::StringEncodingConverterjuce::CharPointer_UTF8,juce::CharPointer_UTF16::convert(const juce::String & s={data=0x0a63ed50 “c:\code\Working2\SoundBetter\runfiles\cache\sb\ec5f28b2c8032fefd04473a6477e6637” }) Line 1996 C++
juce_debug.dll!juce::String::toWideCharPointer() Line 2046 + 0xd bytes C++
juce_debug.dll!juce::File::existsAsFile() Line 124 + 0x49 bytes C++

I have gotten crashes like this in a few different places. It seems that they always seem to involve a File::existsAsFile or File::getFullPathName, so it looks like it is probably file related.

I don’t fully understand the role of the StringHolder. I assume that it is a class to encapsulate the holding of a string, so that there only needs to be one instance of string when several places use the same value.

If I understand this properly, the bufferFromText function is trying to compute the offset of the StringHolder that contains the string and then returns us a pointer to that StringHolder.

static inline StringHolder* bufferFromText (const CharPointerType& text) noexcept
{
    // (Can't use offsetof() here because of warnings about this not being a POD)
    return reinterpret_cast <StringHolder*> (reinterpret_cast <char*> (text.getAddress())
                - (reinterpret_cast <size_t> (reinterpret_cast <StringHolder*> (1)->text) - 1));
}

I do have another thread that is doing some stuff with StringHolders, which appears to be waiting on a critical section.

msvcr90d.dll!_lock(int locknum=0x00000004) Line 349 C
msvcr90d.dll!operator delete(void * pUserData=0x0ccabdf0) Line 45 + 0x7 bytes C++
msvcr90d.dll!operator delete[](void * p=0x0ccabdf0) Line 21 + 0x9 bytes C++
juce_debug.dll!juce::StringHolder::release(juce::StringHolder * const b=0x0ccabdf0) Line 164 + 0xf bytes C++
juce_debug.dll!juce::StringHolder::makeUniqueWithByteSize(const juce::CharPointer_UTF8 & text={…}, unsigned int numBytes=0x00000005) Line 196 + 0x9 bytes C++
juce_debug.dll!juce::String::preallocateBytes(const unsigned int numBytesNeeded=0x00000004) Line 242 + 0x14 bytes C++
juce_debug.dll!juce::String::appendCharPointerjuce::CharPointer_UTF8(const juce::CharPointer_UTF8 & textToAppend={…}) Line 273 C++
juce_debug.dll!juce::String::operator+=(const juce::String & other={data=0x0ccabfd8 “999” }) Line 625 C++
juce_debug.dll!juce::operator<<(juce::String & s1={data=0x0ccabdf8 “/” }, const juce::String & s2={data=0x0ccabfd8 “999” }) Line 709 + 0xf bytes C++
juce_debug.dll!juce::URL::withNewSubPath(const juce::String & newPath={data=0x0ccabda8 “/999” }) Line 251 + 0x37 bytes C++

So, is it the case that every String belongs to a StringHolder? I don’t see a list of StringHolders anywhere, so I figure that the StringHolder is only able to save memory on a string if we duplicate the string.

So any insight into what might be going on? Any ideas on what I might want to try to narrow down the crash?

Thanks in advance.
John Lawrie


Should we use JUCE_LEAK_DETECTOR in base classes?
#2

StringHolder’s just an internal object used by String, but it’s one of the most heavily used classes in the library, and I’d be 99.99% confident that the bug would lie elsewhere.

So, my guess is that the URL or File object you’re using must be corrupt - most likely a dangling pointer. Or maybe you’re using the same URL/File object from multiple threads without locking?