Friedemann Metzger
Friedemann Metzger

Reputation: 21

C++ Singleton gets instantiated twice - problem with static member variable

I have implemented the standard singleton pattern (the non threaded version) like this:

hpp:

#ifndef SERVICEMANAGER_H
#define SERVICEMANAGER_H

#include <QObject>
#include <QVariant>
#include <QMap>
#include "IService.h"
#include "qhcore_global.h"
#include <QDebug>



class  ServiceManager : public QObject
{
    Q_OBJECT

public:
    static ServiceManager*     instance();
    ~ServiceManager(){qDebug()<<Q_FUNC_INFO;}
    IService*           service(QString name);
    bool                registerService(IService* service);

private:
    explicit            ServiceManager(QObject *parent = nullptr);

    QMap<QString, IService*>    _serviceMap;
    static ServiceManager*      __instance;

signals:
    void serviceAdded(QString serviceName);
public slots:
};



#endif // SERVICEMANAGER_H

cpp:

ServiceManager* ServiceManager::__instance = nullptr;

ServiceManager::ServiceManager(QObject *parent) : QObject(parent)
{
     qDebug()<<"CREATE::CTOR";
}

ServiceManager* ServiceManager::instance()
{
    qDebug()<< __instance << "BEFORE";
    if(__instance == nullptr)
    {
        qDebug()<<"CREATE";
        __instance = new ServiceManager();
        qDebug()<< __instance << "AFTER";
    }
    return __instance;
}

and my log looks like this

[DEBUG    17.11. - 17:50:18:748] --
[DEBUG    17.11. - 17:50:18:748] -- QObject(0x0) BEFORE
[DEBUG    17.11. - 17:50:18:748] -- CREATE
[DEBUG    17.11. - 17:50:18:748] -- CREATE::CTOR
[DEBUG    17.11. - 17:50:18:748] -- ServiceManager(0x55e9fc6fb510) AFTER
[DEBUG    17.11. - 17:50:18:748] -- 0
[INFO     17.11. - 17:50:18:748] -- "Service registered: lab"
[DEBUG    17.11. - 17:50:18:748] -- 1
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/logs"
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/users"
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/permissions"
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/cards"
[INFO     17.11. - 17:50:43:973] -- "::ffff:192.168.1.32"  has connected.
[INFO     17.11. - 17:50:43:985] -- "admin logged in. (" 1  sessions open)
[INFO     17.11. - 17:50:43:985] -- Device registered:  "4f2o4o7b1a1r"
[INFO     17.11. - 17:50:44:003] -- Create ListResource with FS Resource Handler "logger/mappings_synclist"
[INFO     17.11. - 17:50:44:005] -- Create ListResource with FS Resource Handler "labcontrol/logs_synclist"
[INFO     17.11. - 17:50:44:007] -- Create ListResource with FS Resource Handler "labcontrol/users_synclist"
[INFO     17.11. - 17:50:44:009] -- Create ListResource with FS Resource Handler "labcontrol/permissions_synclist"
[INFO     17.11. - 17:50:44:011] -- Create ListResource with FS Resource Handler "labcontrol/cards_synclist"
[DEBUG    17.11. - 17:50:45:136] -- QObject(0x0) BEFORE
[DEBUG    17.11. - 17:50:45:136] -- CREATE
[DEBUG    17.11. - 17:50:45:136] -- CREATE::CTOR
[DEBUG    17.11. - 17:50:45:137] -- ServiceManager(0x55e9fc749340) AFTER
[DEBUG    17.11. - 17:50:45:137] --  COUNT: 0
[DEBUG    17.11. - 17:50:45:137] -- ()
[WARNING  17.11. - 17:50:45:138] -- "Unavailable servcie: lab"

In the log you can see that the singleton will be instantiated twice. The second time I call the instance() function, the private static member __instance holds a nullptr again. This is totally strange. The same code runs perfectly on other machines with different platforms (OSx, Raspbian, ...).

My system is a beelink BT3 pro running Ubuntu Server 18.10, x64. The code itself is a WebSocket based IoT Cloud which uses Qt5 in combination with plugins.

What I tried:

using different compilers such as gcc-8, gcc-7 and gcc-6. Compiling Qt by my own to ensure that all binaries are built with the same compiler.

Again: My App uses plugins. It probably has something to do with the fact that the singleton object is used within different plugins, loaded at runtime from shared object files via QPluginLoader.

edit: I don't use threads and I don't use different namespaces.

Any hint is welcome! Thanks in advance!

EDIT2 (SOLUTION):

(moved to my answer below.)

Upvotes: 0

Views: 872

Answers (3)

Friedemann Metzger
Friedemann Metzger

Reputation: 21

Unfortunately the answers before didn't worked ... Still the same problem. BUT.. I found the problem:

In my application are a couple of independent plugins and the core plugin. The last one is both: A plugin and a shared library for all the other plugins. My application has a relative search path where it looks for plugins to load. So far so good. But the independent extension plugins need to find the core plugin to link against at runtime. So I copied the core plugin to /usr/lib. This was the issue. There were two copies of the libCorePlugin.so - one in the plugin folder and the other one in /usr/lib. I don't know why this has worked on all the other machines... But let LD_LIBRARY_PATH point to the common plugin folder instead of copying the file to /usr/lib solved the problem.

Does anyone has an idea why this has worked on some systems before?

Upvotes: 0

tunglt
tunglt

Reputation: 1072

You should use the macro Q_GLOBAL_STATIC() to share the instance between plugins. The cpp file can be rewritten like this :

   Q_GLOBAL_STATIC(ServiceManager, myServiceManagerInstance)

    ServiceManager *ServiceManager::instance()
    {
            return myServiceManagerInstance();
    }

Upvotes: 2

Kit.
Kit.

Reputation: 2406

If you statically link your ServiceManager.cpp (or ServiceManager.o) to multiple dynamic libraries, each of them may end up containing its own instance of ServiceManager::__instance.

The solution for that would be to put ServiceManager.o into a separate dynamic library available to all your "plugin" libraries.

Upvotes: 1

Related Questions