开发者

Strange run-time errors with a multi-threaded C++ Qt application exhibiting in VS debugger only

I'm writing a little hobby application in C++ using Qt. The application has to read some dictionary files at startup which takes some time, so I created a custom thread class to parse the dictionaries in the background:

class SetupThread : public QThread
{
    Q_OBJECT
public:
    SetupThread(QObject *p_parent);
    void setDictOutputs(WordDictionary *word, KanjiDictionary *kanji, RadicalDictionary *rad);
    void run()
    {
       emit message("Parsing JMdict dictionary...");
       m_wordDict->parseDictionary("dictionaries/JMdict_e.xml");

       emit message("Parsing KANJIDIC dictionary...");
       m_kanjiDict->parseDictionary("dictionaries/kanjidic2.xml");

       emit message("Parsing RADKFILEX dictionary...");
       m_radDict->parseDictionary("dictionaries/radkfilex.utf8");
    }

signals:
    void message(const QString &p_msg);

private:
    WordDictionary *m_wordDict;
    KanjiDictionary *m_kanjiDict;
    RadicalDictionary *m_radDict;
};

The three "Dictionary" classes are created by me and they all inherit a common interface, which includes Q_OBJECT functionality to signal the main class over the head of the setup thread in Qt::QueuedConnection mode with progress updates while parsing, so it can display a progress bar. The setup thread is invoked from the main classes' constructor like this:

MainForm::MainForm(QWidget *parent, Qt::WFlags flags)
{
    /* ... */
    m_wordDict = new WordDictionary(this);
    m_kanjiDict = new KanjiDictionary(this);
    m_radDict = new RadicalDictionary(this);
    m_setupThread = new SetupThread(this);
    m_setupThread->setDictOutputs(m_wordDict, m_kanjiDict, m_radDict);
    m_setupThread->start();
}

I started having some trouble with the application crashing on exit and I couldn't see what the problem was so I tried to run it in the Visual C++ 2008's debugger. Then I开发者_C百科 get a huge crash during start-up:

Unhandled exception at 0x7568b727 in kanjiflash.exe: Microsoft C++ exception: std::exception at memory location 0x024ffa1c..

The stack trace shows:

KernelBase.dll!7568b727()

[Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]

KernelBase.dll!7568b727()

msvcr90d.dll!_heap_alloc_dbg_impl(unsigned int nSize=72, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0, int * errno_tmp=0x024ff9f8) Line 497 + 0xc bytes C++

msvcr90d.dll!_nh_malloc_dbg_impl(unsigned int nSize=72, int nhFlag=0, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0, int * errno_tmp=0x024ff9f8) Line 239 + 0x19 bytes C++

msvcr90d.dll!_nh_malloc_dbg(unsigned int nSize=72, int nhFlag=0, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0) Line 296 + 0x1d bytes C++

msvcr90d.dll!malloc(unsigned int nSize=1) Line 56 + 0x15 bytes C++

020bea68()

kanjiflash.exe!SetupThread::run() Line 391 + 0x2c bytes C++

QtCored4.dll!QThreadPrivate::start(void * arg=0x020bd0c8) Line 317 C++

msvcr90d.dll!_callthreadstartex() Line 348 + 0xf bytes C

msvcr90d.dll!_threadstartex(void * ptd=0x020bd8f0) Line 331 C

kernel32.dll!75593677()

ntdll.dll!77739d72()

ntdll.dll!77739d45()

The particular line in SetupThread::run() this refers to is the one where I try to run parseDictionary("..."). This call stack is obtained from under Windows7 64bit. In Windows XP 32bit I had an identical problem, only difference was that the stack went to the constructor of a QString(const char *ch) from SetupThread::run(), where it complained and showed the *ch buffer to be a few characters of garbage.

Now the strange thing is this only happens inside the debugger. Both the Debug and Release configurations work fine outside the debugger. While fumbling with the application investigating this, I found and fixed the error which made me use the debugger in the first place but I'm wondering what is really happening and what will I do if I need to use the debugger some day. As I am not fully versed in multi-threaded programming, I'm not sure if it's even possible to run them in a debugger meaningfully, or perhaps I'm doing something wrong, like working on pointers of the main class in the worker thread (access violation?). Any insight greatly appreciated.


I stumbled on an answer today while stepping through the program in the debugger. Based on this stack trace I got before:

kernel32.dll!7d4e2366()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
kernel32.dll!7d4e2366()
QtCored4.dll!QString::QString(const char * ch=0x0265ff24) Line 427 + 0x12 bytes C++
KanjiFlash.exe!SetupThread::run() Line 400 + 0x2c bytes C++
QtCored4.dll!QThreadPrivate::start(void * arg=0x02137f68) Line 317 C++
msvcr90d.dll!_callthreadstartex() Line 348 + 0xf bytes C
msvcr90d.dll!_threadstartex(void * ptd=0x02138828) Line 331 C
kernel32.dll!7d4dfe21()

...and seeing the "const char *ch" buffer contain garbage after the crash, I'd assumed the string literal somehow got corrupted and that was the cause of the exception. But I tried stepping into the first parseDictionary() function and it worked. The beggining of the function looks like this:

void WordDictionary::parseDictionary(const QString &p_dictPath)
{
    // open XML file
    QFile file(p_dictPath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        throw std::exception("Unable to open dictionary file in WordDictionary constructor");
    }
    /* ... */

I noticed the crash occured after this exception was thrown. Apparently Qt doesn't go along well with exceptions so this went unhandled and the message was silently discarded, but the exception crashed the application anyway. The reason this happened inside the debugger was that VS sets the working directory for a debugged binary in the project's directory rather than the output directory. I had a copy of my dictionaries/ directory in the "Debug" and "Release" output directories only so the files couldn't be found which caused the crash. When run from either the "Debug" or "Release" directories, they were opened correctly and nothing happened.

I made a copy of the dictionaries/ directory in the project directory also and it ran under the debugger without a problem. Furthermore, I removed all exception throwing directives from my code to avoid problems in the future (they don't seem to work well in Qt applications). I am not making this an accepted answer however, because I'd still like to know:

  1. Why didn't the exception thrown from the parser get caught anywhere (main(), MainForm::MainForm())? Did Qt do this and if so, how? Can you use exceptions in multithreaded code anyway?
    Edit: answer: I just found out exceptions can't cross thread boundaries. My try-catch directives were placed in main around the whole contraption and inside the MainForm's constructor where I called SetupThread::run(). When I put a try-catch block in run() itself, the contents of the catch block were correctly executed when an exception was thrown.
  2. Why did I get such a confusing stack trace output, leading me on a wild goose chase? It looked like there were problems with creating a QString from a const char * and/or with a malloc somewhere. I developed many "interesting" theories on the probable causes of such errors before I found out what really caused this...
    Actually, the stack traces from the debug-symbols-free Release made more sense now in retrospect: there were references to something like _CxxExceptionThrown in it and no references to either malloc or QString, but I just assumed it lacked the info to show the origin.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜