How can I ensure a dll is not unloaded while any objects in it exist?
The problem is, when I was using explict memory management I could delete the dll objects before freeing the dll, however with smart pointers I have no controll over the order there destroyed, meaning the dll may be freed first causeing a crash when trying to free one of the other objects:
FlPtr is a simple refrence counting class thats calls AddRef and Release as needed
ExampleDll *dll = LoadDll(L"bin\\example.dll");
IObject *obj = dll->CreateObject();
...
obj->Release();
delete dll;//fine because all objects already deleted
return 0;
auto_ptr<ExampleDll> dll = LoadDll(L"bin\\example.dll");
FlPtr<IObject> obj = dll->CreateObject();
...
return 0;//crash if dll is destructed before obj since Object::Release needs to call into the dll
I tried making the dll handle unloading itsself, ie only unload after all objects have been deleted. This work by creating a new object IExampleDll which the dll implements. This is like the ExampleDll object from before but lives in the dll rather than the exe and is also refrence counted. Each object in the dll increments this refrence on contruction and deincrements it on destruction. This means the refrence count only reaches zero when the exe has Released its refrences AND all the dlls objects have been destroyed. It then deletes itsself calling FreeLibrary(GetModuleHandle()) in its destructor.
This however crashes at the FreeLibrary, im asuming because the thread is still in the dlls code that is being unloaded...
I'm at a loss now how to make sure the dll is only unloaded when there are no remaining objects, apart from going back to freeing the dll explicitly after everything else should have been deleted;
int main()
{
ExampleDll *dll = LoadDll("bin\\example.dll");
restOfProgram();
delete dll;
}
This approach becomes difficult when dlls need to be loaded/unloaded mid program saftly, ie if the user changed from d3d to openGL in options.
For the case where the DLL is switched at run time, I'd avoid the smart pointer system for objects created by the DLL and use a system like this:
The application holds a list of all the allocated abstraction layer objects. The abstraction layer objects are the only objects that are allowed to own pointers to objects created by the implementation layer. When swapping DLLs, first iterate all abstraction layer objects and tell them to release the implementation specific data. Then unload the DLL and load the new DLL. Then iterate the abstraction layer objects again and tell them to create new implementation specific data.
Ok I think the best choice is to use the COM approach of polling the dll to see if it can be unloaded. How can I go about doing this though so that I can continue polling the dll after everything else has closed down (ie the main thread has terminated)? Do I need to create a completly seperate process to do this, in which case how do I do it so this seperate process knows about all the loaded dlls, and in a way which has VERY little impact on proformance?
Mayby I could just create the polling system when all the DllPtr's are out of scope and terminate it as soon as it has free the dll? That way it only exists for as long as it takes for any remaing smart pointers to be destroyed.
MSDN is explicit on this topic: "A thread that must unload the DLL in which it is executing and then terminate itself should call FreeLibraryAndExitThread instead of calling FreeLibrary and ExitThread separately. Otherwise, a race condition can occur. For details, see the Remarks section of FreeLibraryAndExitThread.
Assuming you do not want to terminate the thread when unloading the library (otherwise, see MSalters), you need to free the library from the caller that loaded it.
COM solves that by an in-DLL instance counter (much like yours, if I understand you correctly), and regulary checking it by calling a global exported
CanUnloadNow
function.Another option is to have your object/interface smart pointers ALSO reference the DLL they came from. This would increase the client data size, but you wouldn't need to touch the DLL. You might even recycle the LoadLibrary/FreeLibrary reference counter, however that might hit performance.
Also, none of these schemes help much if you get circular DLL dependencies (Component DllA.X references DllB.Y, which references DllA.Z). I haven't yet fould a good solution to that that doesn#t requrie global knowledge.