[Interest] Should we unload() Qt plugins?

Stephen Kelly steveire at gmail.com
Sun Sep 21 15:24:12 CEST 2014


Hello,

Here is a simple plugin interface:

    #ifndef PluginIFace_H
    #define PluginIFace_H

    #include <QObject>

    class PluginIFace
    {
    public:
      virtual ~PluginIFace() {}

      // Caller takes ownership of the returned QObject*
      virtual QObject* getObject() = 0;
    };

    Q_DECLARE_INTERFACE(PluginIFace, "com.example.PluginIFace/1.0" )

    #endif

and a concrete plugin which returns a custom SomeObject:

    #ifndef MyPlugin_H
    #define MyPlugin_H

    #include "pluginiface.h"

    class SomeObject : public QObject
    {
      Q_OBJECT
    public:
      SomeObject(QObject *parent = 0);
    };

    class MyPlugin : public QObject, public PluginIFace
    {
      Q_OBJECT
      Q_PLUGIN_METADATA(IID "com.example.PluginIFace")
      Q_INTERFACES(PluginIFace)
    public:
      MyPlugin(QObject *parent = 0);

      QObject* getObject() Q_DECL_OVERRIDE;
    };

    #endif

And its implementation:

    #include "myplugin.h"

    SomeObject::SomeObject(QObject *parent)
      : QObject(parent)
    {

    }

    MyPlugin::MyPlugin(QObject *parent)
      : QObject(parent)
    {

    }

    QObject* MyPlugin::getObject()
    {
      return new SomeObject;
    }

And a user of the plugin:

    QCoreApplication app(argc, argv);

    QPluginLoader pl(BUILD_DIR "/libmyplugin.so");
    pl.load();
 

With the above code, valgrind reports:

    HEAP SUMMARY:
        in use at exit: 5,380 bytes in 37 blocks
      total heap usage: 162 allocs, 125 frees, 22,715 bytes allocated

    32 bytes in 1 blocks are still reachable in loss record 12 of 37
      at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
      by 0x691068F: _dlerror_run (dlerror.c:141)
      by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
      by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
      by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
      by 0x51567F5: QLibraryPrivate::loadPlugin() (qlibrary.cpp:585)
      by 0x514C058: QPluginLoader::load() (qpluginloader.cpp:240)
      by 0x400E71: main (in 
/home/stephen/safe/dev/playground/plugintest/build/main)

    64 bytes in 1 blocks are still reachable in loss record 25 of 37
      at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
      by 0x4007E06: expand_dynamic_string_token (dl-load.c:162)
      by 0x4008D71: _dl_map_object (dl-load.c:2538)
      by 0x4014A53: dl_open_worker (dl-open.c:235)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x40143BA: _dl_open (dl-open.c:661)
      by 0x691002A: dlopen_doit (dlopen.c:66)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x691062C: _dlerror_run (dlerror.c:163)
      by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
      by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
      by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)

    64 bytes in 1 blocks are still reachable in loss record 26 of 37
      at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
      by 0x400BDC3: _dl_new_object (dl-object.c:165)
      by 0x4006783: _dl_map_object_from_fd (dl-load.c:1059)
      by 0x4008DFF: _dl_map_object (dl-load.c:2605)
      by 0x4014A53: dl_open_worker (dl-open.c:235)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x40143BA: _dl_open (dl-open.c:661)
      by 0x691002A: dlopen_doit (dlopen.c:66)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x691062C: _dlerror_run (dlerror.c:163)
      by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
      by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)

    144 bytes in 1 blocks are still reachable in loss record 32 of 37
      at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
      by 0x40118A2: _dl_check_map_versions (dl-version.c:293)
      by 0x4014EEC: dl_open_worker (dl-open.c:278)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x40143BA: _dl_open (dl-open.c:661)
      by 0x691002A: dlopen_doit (dlopen.c:66)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x691062C: _dlerror_run (dlerror.c:163)
      by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
      by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
      by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
      by 0x51567F5: QLibraryPrivate::loadPlugin() (qlibrary.cpp:585)

    1,232 bytes in 1 blocks are still reachable in loss record 36 of 37
      at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
      by 0x400BB23: _dl_new_object (dl-object.c:75)
      by 0x4006783: _dl_map_object_from_fd (dl-load.c:1059)
      by 0x4008DFF: _dl_map_object (dl-load.c:2605)
      by 0x4014A53: dl_open_worker (dl-open.c:235)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x40143BA: _dl_open (dl-open.c:661)
      by 0x691002A: dlopen_doit (dlopen.c:66)
      by 0x400FFF3: _dl_catch_error (dl-error.c:187)
      by 0x691062C: _dlerror_run (dlerror.c:163)
      by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
      by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)

    LEAK SUMMARY:
      definitely lost: 0 bytes in 0 blocks
      indirectly lost: 0 bytes in 0 blocks
        possibly lost: 0 bytes in 0 blocks
      still reachable: 1,536 bytes in 5 blocks
            suppressed: 3,844 bytes in 32 blocks

If I add 

    pl.unload();

below the 

    pl.load();

in the sample code above, some of the 'still reachable' reports go away:

    Memcheck, a memory error detector
    Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
    Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
    Command: ./main


    HEAP SUMMARY:
        in use at exit: 3,876 bytes in 33 blocks
      total heap usage: 162 allocs, 129 frees, 22,715 bytes allocated

    32 bytes in 1 blocks are still reachable in loss record 12 of 33
      at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
      by 0x691068F: _dlerror_run (dlerror.c:141)
      by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
      by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
      by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
      by 0x51567F5: QLibraryPrivate::loadPlugin() (qlibrary.cpp:585)
      by 0x514C058: QPluginLoader::load() (qpluginloader.cpp:240)
      by 0x400ED1: main (in 
/home/stephen/safe/dev/playground/plugintest/build/main)

    LEAK SUMMARY:
      definitely lost: 0 bytes in 0 blocks
      indirectly lost: 0 bytes in 0 blocks
        possibly lost: 0 bytes in 0 blocks
      still reachable: 32 bytes in 1 blocks
            suppressed: 3,844 bytes in 32 blocks


So, maybe we should unload() Qt plugins?

However, the plugin interface contains

    // Caller takes ownership of the returned QObject*
    virtual QObject* getObject() = 0;

If we do something like 

    QCoreApplication app(argc, argv);

    QPluginLoader pl(BUILD_DIR "/libmyplugin.so");
    pl.load();
    QObject *pluginObject = qobject_cast<PluginIFace*>(pl.instance())-
>getObject();
    
    pl.unload();
    delete pluginObject;

Then we get a segfault because the destructor of the object pointed to 
by pluginObject was defined in the plugin, which was just unloaded, so the
destructor doesn't exist anymore.

In more complex programs, it is difficult to know whether it is safe to 
call pl.unload() because we might not know whether all objects created 
through the plugin interface have already been destroyed.

So, should the guideline be: "Never call QPluginLoader::unload()", and 
ignore 'still reachable' reports from valgrind?

Thanks,

Steve.





More information about the Interest mailing list