Discussion:
[Interest] Is it ok to use QLibrary to load and unload different shared libraries in sequence?
Roland Winklmeier
2018-10-24 09:58:25 UTC
Permalink
Hi there,

I have a quick question about QLibrary. I'm connecting to a 3rd party
application software by using the 3rd party application's SDK. That SDK is
a single proprietary Windows dll and there is a different dll for each
version of the application. I would like to support the connection to
several versions of the application at runtime, hence I have all the dll
files in my bin folder:

bin/ApplicationConnect-4.0.dll
bin/ApplicationConnect-4.1.dll
bin/ApplicationConnect-4.2.dll
...

At runtime, depending on the user configuration, any of those versions are
loaded and symbols resolved using e.g. QLibrary
myDll("ApplicationConnect-4.0"). That all works perfectly fine. Now I would
like to add the feature to switch versions at runtime and here is my
question:

Is it fine to reuse the same QLibrary instance in the following sequence:
QLibrary myDll("ApplicationConnect-4.0");
myDll.load()
[... resolve and do stuff ...]
[... User configured different version...]
myDll.unload
myDll.setFileName("ApplicationConnect-4.1")
myDll.load()
[ ... resolve symbols and continue using it...]

or shall I destroy the previous QLibrary instance and create a new one?

I got some strange behavior while debugging the above lines in QtCreator
(error message saying that "Command aborted" after the first unload and gdb
aborts). So I'm not sure if I do something totally wrong.

Thanks,
Roland
Thiago Macieira
2018-10-24 15:19:44 UTC
Permalink
Post by Roland Winklmeier
At runtime, depending on the user configuration, any of those versions are
loaded and symbols resolved using e.g. QLibrary
myDll("ApplicationConnect-4.0"). That all works perfectly fine. Now I would
like to add the feature to switch versions at runtime and here is my
QLibrary myDll("ApplicationConnect-4.0");
myDll.load()
[... resolve and do stuff ...]
[... User configured different version...]
myDll.unload
myDll.setFileName("ApplicationConnect-4.1")
myDll.load()
[ ... resolve symbols and continue using it...]
or shall I destroy the previous QLibrary instance and create a new one?
This should work.
Post by Roland Winklmeier
I got some strange behavior while debugging the above lines in QtCreator
(error message saying that "Command aborted" after the first unload and gdb
aborts). So I'm not sure if I do something totally wrong.
The problem might be a number of different things. In general, don't unload
libraries. If your design requires it, rethink and redesign.

Things to watch out for:
1) The number of load() and unload() calls must be paired, since they control
a refcount. Don't just destroy the QLibrary without unload().

2) make sure that there are no pointers remaining to any function, constant or
variable left that points to something in that library

3) make sure that no plugin was loaded that linked to that library or, if it
was, unload it too (preferably, don't unload anything).

4) make sure the libraries don't do anything stupid in their DllMain unattach
call.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Roland Winklmeier
2018-10-25 07:03:32 UTC
Permalink
Thanks a lot for your response.

Am Mi., 24. Okt. 2018 um 17:20 Uhr schrieb Thiago Macieira <
Post by Thiago Macieira
Post by Roland Winklmeier
I got some strange behavior while debugging the above lines in QtCreator
(error message saying that "Command aborted" after the first unload and
gdb
Post by Roland Winklmeier
aborts). So I'm not sure if I do something totally wrong.
The problem might be a number of different things. In general, don't unload
libraries. If your design requires it, rethink and redesign.
It was more like a safety feature. Technically I don't have to unload the
other dlls. I could also keep the previous one loaded, but I was concerned
about potential conflicts. I'm not an expert in these things, but if there
are two or even more shared libraries loaded into the same process that are
almost identical and therefore many common symbol names (the library is
proprietary so I have no way to check, but the publisher says so) can I run
into issues? If not, I would not bother unloading them.
Post by Thiago Macieira
1) The number of load() and unload() calls must be paired, since they control
a refcount. Don't just destroy the QLibrary without unload().
This is done. The call to unload() is done explicitly before QLibrary is
destroyed (I keep QLibrary in a QScopedPointer that is reset after
unloading).

2) make sure that there are no pointers remaining to any function, constant
Post by Thiago Macieira
or
variable left that points to something in that library
Also done. All resolved function pointers are cleared and set to nullptr
before unloading.
Post by Thiago Macieira
3) make sure that no plugin was loaded that linked to that library or, if it
was, unload it too (preferably, don't unload anything).
The libraries are only resolved in one line of code, so this should not be
a problem.
Post by Thiago Macieira
4) make sure the libraries don't do anything stupid in their DllMain unattach
call.
That's hard to check since I don't have the source code. Or is there
another way to check that?

Just as a side note: When running without debugger, everything is fine.
Only the during debugging, I see the error message. I might just try in a
very simple test case, whether I can reproduce it with gdb.
Thiago Macieira
2018-10-26 16:21:00 UTC
Permalink
Post by Roland Winklmeier
Post by Thiago Macieira
The problem might be a number of different things. In general, don't unload
libraries. If your design requires it, rethink and redesign.
It was more like a safety feature. Technically I don't have to unload the
other dlls. I could also keep the previous one loaded, but I was concerned
about potential conflicts. I'm not an expert in these things, but if there
are two or even more shared libraries loaded into the same process that are
almost identical and therefore many common symbol names (the library is
proprietary so I have no way to check, but the publisher says so) can I run
into issues? If not, I would not bother unloading them.
Loading two versions of the same library is even worse and far more likely to
cause problems. When I suggested you redesign, that would be part of it and
you'd need to figure out a way so that you only ever load one and don't unload
it.

A process separation idea comes to mind.
Post by Roland Winklmeier
2) make sure that there are no pointers remaining to any function, constant
Post by Thiago Macieira
or
variable left that points to something in that library
Also done. All resolved function pointers are cleared and set to nullptr
before unloading.
No "new'ed" objects that it may have allocated that need "delete"? Note that
on Windows, different runtimes have different malloc() pools. Polymorphic
classes have pointers to the vtable, which is in the DLL. And so forth.
Post by Roland Winklmeier
Post by Thiago Macieira
4) make sure the libraries don't do anything stupid in their DllMain unattach
call.
That's hard to check since I don't have the source code. Or is there
another way to check that?
There are ways, but difficult ones. You'd have to debug through the assembly
to figure out what it is doing.
Post by Roland Winklmeier
Just as a side note: When running without debugger, everything is fine.
Only the during debugging, I see the error message. I might just try in a
very simple test case, whether I can reproduce it with gdb.
Interesting.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Till Oliver Knoll
2018-10-26 17:49:57 UTC
Permalink
Post by Thiago Macieira
Post by Roland Winklmeier
Post by Thiago Macieira
The problem might be a number of different things. In general, don't unload
libraries. If your design requires it, rethink and redesign.
It was more like a safety feature. Technically I don't have to unload the
other dlls. I could also keep the previous one loaded, but I was concerned
about potential conflicts. I'm not an expert in these things, but if there
are two or even more shared libraries loaded into the same process that are
almost identical and therefore many common symbol names (the library is
proprietary so I have no way to check, but the publisher says so) can I run
into issues? If not, I would not bother unloading them.
Loading two versions of the same library is even worse and far more likely to
cause problems. When I suggested you redesign, that would be part of it and
you'd need to figure out a way so that you only ever load one and don't unload
it.
The OP wrote: „Now I would like to add the feature to switch versions at runtime“

It is my understanding that we are talking about 3rd party libraries over which the OP has no source control, so there is nothing to redesign.

Also we are talking about „the same library, im different versions“, or in other words: „4.1 being mostly the same as 4.0, but with added features“ (but each implementation in its distinct DLL, of course).

I‘m not sure what would happen if you would load those two DLLs (with „mostly the same exported symbol names“): I‘m pretty sure that at link time for sure would get „duplicate symbols“ linker errors. But when using QLibrary? You would explicitly lookup (resolve) the required symbols „by application logic“ (dlopen etc.), and assign them to distinct function pointers etc., so maybe that would even work.

(In fact, it /must/ work: for „plugins“ you typically have a „create“ and „destroy“ C-syle function that are dynamically resolved, and of course each such „plugin“ (DLL) has those same function declarations).

But I would /still/ want to unload a „plugin“ that is no longer required, especially for „design“ reasons. Why would you want to keep that „dead code“ in memory if the user has decided - at runtime - to use „v4.1“ now instead of the previous 4.0 (or 4.2)? Waste of resources.

(The argument „what if the user wants to switch back fast?“ could be solved with a simple QTimer and „5 minutes idle plugins get kicked“ approach).

Whether or not the same QLibrary instance can be used to repeatedly load/unload different DLLs I don‘t know: according to Thiago „yes“, and I‘d certainly expect so, too.

Cheers,
Oliver
Thiago Macieira
2018-10-27 00:09:01 UTC
Permalink
Post by Till Oliver Knoll
Post by Thiago Macieira
Loading two versions of the same library is even worse and far more likely
to cause problems. When I suggested you redesign, that would be part of
it and you'd need to figure out a way so that you only ever load one and
don't unload it.
The OP wrote: „Now I would like to add the feature to switch versions at runtime“
It is my understanding that we are talking about 3rd party libraries over
which the OP has no source control, so there is nothing to redesign.
I understand what he wants to do. I am calling on a rethink of that. Either by
isolating in different processes or by not switching at runtime at all.
Post by Till Oliver Knoll
But I would /still/ want to unload a „plugin“ that is no longer required,
especially for „design“ reasons. Why would you want to keep that „dead
code“ in memory if the user has decided - at runtime - to use „v4.1“ now
instead of the previous 4.0 (or 4.2)? Waste of resources.
Because of the complexity of getting it right. You have to weigh the memory
caused by keeping it loaded versus the time it takes you to get it right.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Loading...