|
Boost : |
Subject: [boost] Severe shared_ptr limitation WRT dynamically loaded libraries
From: J.D. Herron (jotadehace_at_[hidden])
Date: 2008-11-19 17:37:39
Hello, I would like to raise what I think is a serious limitation of the
boost shared_ptr implementation. I have thought a bit about solutions but
don't have anything concrete to offer as yet but would be interested in
seeing the thoughts of the boost library developers. I work in an
architecture that uses dynamically loaded factory patterns quite a lot and
factories can also be dynamically unloaded. This also makes heavy use of
shared_ptrs to make memory management a breeze. The basic problem is that
the boost implementation of the sp_counted_base relies on virtual functions
to deallocate which are implemented by various templates in the detail
namespace. If I have a dynamically loaded factory interface that returns a
shared_ptr, the factory implementation causes the templates to generate the
virtual function code within the factory library. Everything is fine as
long as the library stays loaded but if the library is unloaded, any client
code holding the shared pointer to an object generated by that factory now
has an invalid virtual function table for the sp_counted_base it holds. So
when the reference count goes to zero an access violation is generated. It
is stated that the shared_ptr is to be as close to a raw pointer as possible
which I very much agree with. However this behavior really sets it apart
from raw pointers as it is certainly possible for a factory to return a
dynamically allocated raw pointer to client code, unload the factory library
and have the client code deallocate the raw pointer after the unload with no
problem.
To illustrate the problem in a windows environment, here is a simple
example. Lets say I have a simple factory interface like so:
class Factory
{
public:
virtual shared_ptr<int> GetIntSharedPtr() = 0;
virtual int * GetIntRawPtr() = 0;
};
I implement a factory DLL to provide one of these factories that has an
exported function GetFactory() like so:
/// implementation of the Factory interface
class TestFactory : public Factory
{
public:
virtual shared_ptr<int> GetIntSharedPtr()
{
return shared_ptr<int>( new int(5) );
}
virtual int * GetIntRawPtr()
{
return new int(0);
}
};
/// Library instance of the factory implementation
TestFactory g_Factory;
/// Exported function to return the factory for this library
_declspec(dllexport) extern "C" Factory & GetFactory()
{
return g_Factory;
}
This is compiled into a library named "TestFactory.dll". Now I create a
simple command line application that does the following:
/// Typedefinition for for the GetFactory exported function
typedef Factory & (_cdecl * GetFactory)();
int main()
{
// dynamically load the TestFactory library
HMODULE hFactoryDll = ::LoadLibrary( "TestFactory.dll" );
// get the GetFactory() interface from the loaded library
GetFactory pfnGetFactory = ::GetProcAddress( hFactoryDll, "GetFactory"
);
// Acquire the factory object
Factory & factory = pfnGetFactory();
// Call the factory interfaces to get dynamically allocated integers
int * pRawInt = factory.GetIntRawPtr();
shared_ptr<int> spInt = factory.GetIntSharedPtr();
// everything is fine so far, now unload the factory library
::FreeLibrary( hFactoryDll );
// deallocating the raw pointer recieved from the factory works just
fine
delete pRawInt; // THIS WORKS!
// However due to the reasons outlined in this post, releasing the
shared_ptr causes
// an access violation
spInt.reset() // THIS CRASHES!
}
I would like to see a solution that would make this simple example and very
reasonable use case work for shared_ptrs. Note that the argument that the
client code cannot know what the factory is actually returning requires that
the factory library be in memory so that appropriate subclass destructors
can be called does not hold water here. This example uses simple integers.
The root of the issue is not one of the virtual function table for the
wrapped type getting corruped on library unload but rather the virtual
function table for a member of the shared_ptr itself. I will put some
thought toward this but am interested to see what boost developers might be
able to come up with as well.
J.D.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk