0

I want to run multiple MuparserX parsers concurrently using QThreadPool. Here's the code:

#include <iostream>
#include <QRunnable>
#include <QThreadPool>
#include "mpParser.h"

struct Task: public QRunnable {
    void run() override {
        //Create a new parser
        mup::ParserX p;
    }
};

int main(int argc, char *argv[])
{
    for (int i = 0; i != 10; ++i)
        QThreadPool::globalInstance()->start(new Task);

    while (QThreadPool::globalInstance()->activeThreadCount() > 0) {}

    return 0;
}

However, my program crashes with either "list iterators incompatible" or "list iterator not dereferencable" errors in the destructor mup::ParserX::~ParserX(). This happens only with MSVC13 and 15, and only in debug builds; release builds run without error and produce the expected output. GCC debug and release builds both work fine. Is there some nuance of Microsoft's compiler that's causing this, or is my program incorrect?

Carlton
  • 4,217
  • 2
  • 24
  • 40
  • *and only in debug builds;* -- That message only appears in debug builds of Visual C++ programs, not release builds. – PaulMcKenzie Sep 21 '17 at 19:06
  • *This happens only with MSVC13 and 15* -- If it happens on just one version of a single compiler, you need to be concerned that there is something wrong with your program, not the compiler. – PaulMcKenzie Sep 21 '17 at 19:08
  • So, possibly the error is happening in other builds as well, just not visibly? – Carlton Sep 21 '17 at 19:09
  • MSVC has iterator checks in debug mode. I don't know about the other compilers. In release mode, no iterators are checked, thus anything wrong leads to undefined behavior, include seeming to work correctly. – PaulMcKenzie Sep 21 '17 at 19:12
  • Also, why the C-style cast here: `(mup::Variable&)`? What happens if you remove that cast? What, if any, are the compiler errors? If you do get errors, you shouldn't try to cover those errors up by doing a C-style cast to shut the compiler up. – PaulMcKenzie Sep 21 '17 at 19:21
  • The C-style cast was copied from an example. It doesn't affect the result; I have removed it. – Carlton Sep 21 '17 at 19:29
  • Release mode is intended to be fast. All of the nice checking and little initialization/memory clearing games debuggers play to make finding problems easier are gone because all that extra work takes time. So if debug mode finds an error, it's probably also present in the release version but no one is looking for it. This means that while your program looks like it works, it's probably doing something wrong, but survivable. Survivable, right up until you're on stage demonstrating your creation at a tradeshow and suddenly something goes hilariously wrong in front of thousands of people. – user4581301 Sep 21 '17 at 19:39
  • Apparently the lesson of the day has little to do with either Muparser or QThreads. Thanks for your help. – Carlton Sep 21 '17 at 20:13
  • @Carlton, Have you tried using [`QtConcurrent::run`](https://doc.qt.io/qt-5/qtconcurrent.html#run) to start your tasks, and wait for them to finish using a [`QFutureWatcher`](https://doc.qt.io/qt-5/qfuturewatcher.html)? (instead of busy-waiting) – Mike Sep 21 '17 at 20:20
  • 1
    @Mike I get the same crash when queuing with `QtConcurrent::run` and waiting with `QFuture::waitForFinished`. I think the problem is that MuparserX is not thread-safe. – Carlton Sep 22 '17 at 15:07

1 Answers1

0

I have found a work-around for this problem, though not really a clean solution. The error occurs in mup::IToken::~IToken(), when the macro MUP_LEAKAGE_REPORT is defined. The macro (automatically defined in debug builds) invokes a static member in a non-thread-safe way, giving rise to the iterator error. The simple solution is to just comment out the line where the macro is defined. In my version of MuparserX, it is in mpDefines.h, line 100:

  #define MUP_LEAKAGE_REPORT

As far as I can tell, the macro is only used for debugging MuparserX and doesn't have any effect on the behavior of the parser.

Carlton
  • 4,217
  • 2
  • 24
  • 40