Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

static const QString, implicit sharing issue


  • Moderators

    Hi,

    I have a very stange bug I would like to understand more about,

    Qt Version 5.11.2 (can't upgrade to 5.12 unfortunately because it does not happen with 5.12)
    Target OS android.

    I have a header file that contains a bunch of const static QStrings that are used throught the application.

    In my App I have the following line of code:

    QString DefaultPath(dataPath );
    

    where dataPath is the static const QString, this line crashes.

    However when I do the following

    QString DefaultPath(dataPath + "" );
    

    basically forcing a copy, it does not crash.
    It also only crashes on my Samsung device, the HUAWEI one works fine.
    Also also it only crashes in release mode, debug works fine acrosss the board.

    I know how to fix this, but I would like to know more about what is going on here.

    Anyone any idea?


  • Qt Champions 2019

    @J.Hilk said in static const QString, implicit sharing issue:

    dataPath

    Is it a QStringLiteral ?


  • Moderators

    @Christian-Ehrlicher
    no

    static const QString defaultPath    = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QString("/MPR/");
    static const QString dataPath       = defaultPath + QString("Data/");
    

    should it be?


  • Qt Champions 2019

    No, it was just a guess since there are known issues with QStringLiteral in a plugin when the plugin gets unloaded.
    Do you get a useful backtrace? I would maybe change it to

    static QString defaultPath() { return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QString("/MPR/"); }

    To avoid global statics.


  • Moderators

    @Christian-Ehrlicher well, that fixes the issue as well, and is probably the more reasonable fix, than adding an empty QString to dataPath when I use it.

    Lot's of refactoring to do! x)

    Thanks.


  • Lifetime Qt Champion

    @J.Hilk

    And it is the only feasible solution. Having non-POD-types as global variables is undefined behavior, as the constructor of your global static might be run anytime (and you don't know when). Therefore its eays to access the data before it is initialized.

    Regards


  • Moderators

    @aha_1980
    well technically, the only non POD-qualifying trait would be QString not being an intrinsic type - which is enough ;-). Never the less your argument still holds true!


  • Qt Champions 2017

    Actually I'm pretty sure the problem is not so much it being POD or not, but rather that you have dependent initialization.
    dataPath depends on defaultPath but C++ does not guarantee you order of initialization of statics. So dataPath might just be initialized with garbage (i.e. before defaultPath is initialized). Additionally, I think QStandardPaths (I may think wrong) is available only after QCoreApplication is up and running.


  • Qt Champions 2017

    @J-Hilk, could you try something for me btw, just out of curiosity:

    static struct  {
        const QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QString("/MPR/");
        const QString dataPath = defaultPath + QString("Data/");
    } strings;
    

    Does that have the same problem?


  • Moderators

    @kshegunov very true,

    It's probably for the best, if I'll wrap this all in a propper class at this point.
    I centralized all those path for 2 reasons.

    • I had to change 1 path and boy, was that a pain.
    • I wanted to reduze the overall memory consumption of the app.
    1. Still applies,
    2. doesn't matter if it's not static but constructed at runtime.

    legacy code - there's always something x)


    Does that have the same problem?

    I'll test it.


  • Moderators

    @kshegunov said in static const QString, implicit sharing issue:

    @J-Hilk, could you try something for me btw, just out of curiosity:

    static struct  {
        const QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QString("/MPR/");
        const QString dataPath = defaultPath + QString("Data/");
    } strings;
    

    Does that have the same problem?

    It does not! Works fine with the static struct.


  • Qt Champions 2017

    Indeed, so you have your answer then. One note: this "hack" is going to work only with dynamic linking. If you go to static you're going to run into the same stuff, and not in a good way ... ;) Static init is per-module, so having only one module means at that struct's init QStandardPaths::writableLocation's statics may just be initialized sometime in the future.


  • Moderators

    @kshegunov I'm learning something new every day :D

    However, I'll stick to the proper class implementation and inherit from that simple class where the standardpaths are needed. It's the way, where the least refractoring is needed. I'm kind of on a deadline so close to christmas x)

    Thanks all for bringing light to my dark place, where I spend the last couple of days figuring this out, and I'm cutting it close as it is.


  • Qt Champions 2017

    @J.Hilk said in static const QString, implicit sharing issue:

    @kshegunov I'm learning something new every day :D

    Don't we all ... :)

    However, I'll stick to the proper class implementation and inherit from that simple class where the standardpaths are needed.

    Probably the best and most reliable solution.

    I'm kind of on a deadline so close to christmas x)

    I hear you brother! I have to deliver on the 2nd of January.


Log in to reply