|
Boost : |
Subject: Re: [boost] Severe shared_ptr limitation WRT dynamically loadedlibraries
From: J.D. Herron (jotadehace_at_[hidden])
Date: 2008-11-20 11:23:32
Thank you for another good suggestion Jarrad. This could certainly be done
and would get around the problems with some other workarounds but it now
forces all developers of factories to construct the shared_ptrs with special
deleters. Granted this is really just a slight inconvenience but I'm also
dealing with lots of legacy. This is certainly a viable work around but I'd
still like to see a way that the shared_ptr implementation itself could
handle this situation better.
J.D.
On Thu, Nov 20, 2008 at 6:47 AM, Jarrad Waterloo <jwaterloo_at_[hidden]
> wrote:
> When the shared_ptr is constructed pass in a custom deleter which will
> among
> other things increment and decrement the library reference count by calling
> LoadLibrary[Ex] and FreeLibrary. Note using GetModuleHandleEx might be
> better than load library for incrementing reference count as the library
> has
> already been loaded less needs to be done.
>
>
> -----Original Message-----
> From: boost-bounces_at_[hidden] [mailto:boost-bounces_at_[hidden]]
> On Behalf Of J.D. Herron
> Sent: Wednesday, November 19, 2008 5:38 PM
> To: boost_at_[hidden]
> Subject: [boost] Severe shared_ptr limitation WRT dynamically
> loadedlibraries
>
> 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.
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk