0

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.)

  • 1
    Is this cpp file a part of the application or a part of the plugin? – user7860670 Nov 19 '18 at 10:24
  • multithreaded? race condition on initialization? – Matthieu Brucher Nov 19 '18 at 10:24
  • 2
    Unrelated to your problem, but note that symbols starting with double underscores are reserved everywhere for the compiler and standard library. See e.g. [this old answer](https://stackoverflow.com/a/228797/440558) for more details. – Some programmer dude Nov 19 '18 at 10:25
  • -No threads (see edit). This code is part of a plugin. The whole application consists of plugins. The application itself is just a plugin loader. – Friedemann Metzger Nov 19 '18 at 10:27
  • @FriedemannMetzger In SO you do not add SOLVED or similar to the title of your question, the way to point out that a question has been resolved is to mark a response as correct, if none of the answers helped you then we invite you to post an answer and you could mark it as correct in 2 days. :-) – eyllanesc Nov 20 '18 at 01:04

3 Answers3

2

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();
    }
tunglt
  • 1,022
  • 1
  • 9
  • 16
  • Thanks a lot! I will try this out asap. - There are two questions left: a) Why does my current code works on every other machine without that modification and b) The constructor needs to be public. So it isn't a proper singleton pattern anymore. What do you think? There are workarounds with inheritance and friend declaration. But this looks a bit like an ugly workaround .. Isn't it? – Friedemann Metzger Nov 19 '18 at 16:07
  • @FriedemannMetzger: Could you modify the first log line in the instance() to see futher information : `qDebug()<< __instance << &__instance << QThread::currentThreadId() << "BEFORE";` ? – tunglt Nov 19 '18 at 17:19
  • Unfortunately this hasn't worked. But I found the solution (see EDIT2 in my origin post) - Thanks a lot for your help!! – Friedemann Metzger Nov 19 '18 at 21:25
  • @FriedemannMetzger You are welcome, I've seen your edit. Did you try a strace to see how your application load plugins ? – tunglt Nov 19 '18 at 21:28
1

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.

Rhathin
  • 1,176
  • 2
  • 14
  • 20
Kit.
  • 2,386
  • 1
  • 12
  • 14
  • Thanks a lot for your answer. But I think this should already be the case. ServiceManager.cpp is part of the Core plugin. All the other plugins do link dynamically against the Core plugin. – Friedemann Metzger Nov 19 '18 at 16:28
0

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?