Sign in to follow this  
Trillian

Deallocation of DLL-allocated objects

Recommended Posts

Hello! I have a pretty simple question. You know these IUnknown base interfaces used by DirectX (all COM?), IrrLicht and many libraries? I know those are used to ensure that deallocation of the object is done in proper memory space (and ref counting also). But I have a question regarding the implementation : My engine consists of a couple of .libs which might eventually be compiled into .dlls. Thus, I have created a base "Unknown" class which is defined in my "core" library (statically-linked). I want to know where must the "delete this;" code must be placed in order to respect the correct memory space stuff. 1- It could be inlined into the header of Unknown::selfDestroy(), but I don't think this is okay. 2- It could be defined in the .cpp implementation of Unknown::selfDestroy(), but then is the code deleting from the app or the dll? (unknown.cpp is in a statically linked library but derived classes might be in dlls) 3- It could be implemented into each and every of the Unknown-derived classes .cpp files. So, where must this be placed? Also, I'd like to know if linux's Shared Objects share the same rules.

Share this post


Link to post
Share on other sites
The internal mechanics of COM are fairly complex, hard to explain on a single post.

I had not looked at it for a while (there is a good book that explains it fairly well, I believe is Inside COM), but you certainly want the implementation on the .cpp, you do not want it inlined unless you use a secondary memory management layer for it (not just new/delete)

Note that the COM system allocates the memory for you (CoCreateInstance), and it also releases it. You need to provide this on your Unknown code base to make sure that destruction works as expected.

Share this post


Link to post
Share on other sites
I do not want to use COM, but to create a similar Unknown base class to use in my engine.

I just thought about that, it should be okay if the statically-linked core library contains the implementation (the "delete this;"), because anyways the dlls will link to this library, so the code of the core library will be integrated into the dll, thus the dlls will be calling the "delete this" into their own memory space, and it will work correctly... does that make any sense?

Share this post


Link to post
Share on other sites
I don't think you entirely understand the reason for not calling delete from the app. Both the app and the DLL run in the _same_ memory space. However, they may use different systems for heap allocation. Because of this, it's important that if a piece of memory is heap-allocated with the DLL's allocation function it needs to be deallocated with the DLL's deallocation function, and vice versa if it was allocated with the app's allocation function. The COM approach, and the one you're using, is to have all of that be DLL-side. In order for this to happen, you just need to make sure that any new and delete calls on library objects are happening in a .cpp file which was compiled with the library.

EDIT: actually, after rereading your last message I have no idea whether you already knew that. Sooo... HTH. [grin]

Share this post


Link to post
Share on other sites
I understand that you do not want to use COM, my point is that to achieve this behavior COM has to go through lots of tedious details, and that the book I suggested explains COM on the first few chapters from the point of view of what you are trying to do.

What you describe makes sense as long as dll boundaries are not crossed, you do not want to call delete on the wrong memory manager.

Here is the problem that I think could be found with your approach:

unknown.lib (your unknown static lib)

my_physics_dll uses unknown and exposes physics : unknown
my_graphics_dll uses unknown and exposes graphics : unknown

If you create an object on my_physics_dll (Create/AddRef) and delete it (Release) on my_graphics.dll by casting it to unknown you can end up calling the wrong memory manager (unless unknown is pure abstract)

Note also that as long as IUnknown is a pure abstract class then you should be fine since you are exposing only the interface and not the implementation.

Does it make sense?

Share this post


Link to post
Share on other sites
Quote:
Original post by ldeejDoes it make sense?


It does. That was clear, it answers my question. I will make Unknown pure virtual.... here, I'll toss a random implementation:


class Unknown
{
private:
unsigned int m_RefCount;

protected:
virtual ~Unknown() {}

virtual void selfDestroy() = 0; // Implementation { delete this; } in derived classes.

public:
inline Unknown() : m_RefCount(0) {}

inline void grab() { ++m_RefCount; }
inline void drop() { if(--m_RefCount == 0) selfDestroy(); }

inline unsigned int getRefCount() const { return m_RefCount; }
};




Is this implementation okay? Is it ready to be used for my dll stuff?

Thanks

Share this post


Link to post
Share on other sites
There's no reason to make selfDestroy virtual, as long as the destructor is virtual.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
There's no reason to make selfDestroy virtual, as long as the destructor is virtual.


Thanks. I'll fix that. I'll inline it like the other functions.

Share this post


Link to post
Share on other sites
SelfDestroy needs to be virtual to make sure the destruction respects memory allocations across dll boundaries.

This is only half the equation and this is the easy part, the hard part is allocating objects. What do you have in mind for that.

Share this post


Link to post
Share on other sites
Quote:
Original post by ldeej
SelfDestroy needs to be virtual to make sure the destruction respects memory allocations across dll boundaries.

Ohh, I see. You're putting the base class and the derived class in different DLLs. In that case you're right about the virtual (though if it were me, I'd put the allocation and deallocation both in the Unknown class, via a custom allocation function).

Actually... hmm. I don't think it would need to be virtual even then, since the compiler pastes the memory deallocation onto the destructor code rather than at the callsite of the destructor.

Share this post


Link to post
Share on other sites
Quote:
Actually... hmm. I don't think it would need to be virtual even then, since the compiler pastes the memory deallocation onto the destructor code rather than at the callsite of the destructor.

I'm fairly certain this is incorrect since forced calls to destructors "object->~object()" don't result in memory being freed. In fact the whole point of the original question was to prevent the memory manager of one module going looking for an object pointer from another module, if this mechanism worked then we'd never need to bother.

Jans.

Share this post


Link to post
Share on other sites
BTW, what sort of an environment are you expecting to use this in? If you're planning to use this interface between different compilers, none of this will work because of ABI incompatibilities. And if you're doing it all on one compiler, as long as you use a DLL-compatible runtime (as is default for recent visual studio versions) there's nothing to worry about at all and you don't need special deletion functions.

Share this post


Link to post
Share on other sites
Now I'm getting a bit confused here. Let me make myself clear :

CORE - Static Library, constains the base "Unknown" class.
FOOBAR - Dynamic library, links to CORE and derives it's classes from CORE'S "Unknown" class.
GAME - Application, links to CORE and uses FOOBAR.

Now, if the "delete this;" are inlined into CORE, when FOOBAR will compile, the "delete this;" code will be pasted into the FOOBAR dll. As such, it will be the foobar itself deleting it's own allocated memory, and that'd work...

No.

Writing this I saw the problem : even if FOOBAR can deallocate it's own memory correctly, the GAME linking to CORE will inline the "delete this;" and delete the FOOBAR objects as if it was in the GAME's memory thing.

So I need to use pure virtuals.

Share this post


Link to post
Share on other sites
The easiest solution to this is to make your base class pure and force the reference counting in at instantiation time, like this:


// Pure base
class Unknown
{
public:
virtual void grab()=0;
virtual void drop()=0;
};

// Your class
class MyClass : Unknown
{
// MyClass stuff, but do not implement Unknown
};


// Instantiation wrapper
template <typename BASE>
class Dynamic : public BASE
{
int m_iRefCount;
public:
Dynamic() { m_iRefCount = 0; }
void grab() { m_iRefCount++; }
void drop() { if ( --m_iRefCount == 0 ) delete this; }
};




So... in the DLL that _provides_ these objects you can instantiate the object as follows:


MyClass *CreateMyClass() // Example only - returning refcounted objects like this is bad form.
{
MyClass *pMyClass = new Dynamic<MyClass>();
pMyClass->grab();
return pMyClass;
}



This has the hidden benefit that if MyClass multiply inherits 'unknown' through two different paths, it all still works...

Jans.

Share this post


Link to post
Share on other sites
No, it would not work if you inherit from Unknown from different parents, the call will be ambiguous.

EDIT: Unless you use virtual inheritance for Unknown obviously :-)

Share this post


Link to post
Share on other sites
This is what I use for most of classes (apart from very simple one's):

// .h file
class MyClass
{
public:
static MyClass* create();
void release();
protected:
// Make sure that the only way to create and destroy the object is by using the create/release pair.
MyClass();
~MyClass();
};

// .cpp file
MyClass*
MyClass::create()
{
return new MyClass;
}

void
MyClass::release()
{
delete this;
}



This makes sure that the object can only be alloacted and deallocated within the same compilation unit.
Done it in this way for years now (very often using DLL's) without any problems.

If you want to add ref counting, com style intefaces on top of that, it's up to you.
But to make sure that a class can be used with DLL's the above seems to be enough.

Share this post


Link to post
Share on other sites
What about this :


// Unknown.hpp
class Unknown
{
private:
int m_RefCount;

inline Unknown() : m_RefCount(0) {} // Private constructor
virtual ~Unknown() {} // Private destructor

protected:

virtual selfDestroy() = 0;

public:
inline void grab() { ++m_RefCount; }
inline void drop() { if(--m_RefCount <= 0) selfDestroy(); }

inline int getRefCount() const { return m_RefCount; }

template<class> friend class UnknownBase; // Whatever the syntax for this is
};

template<class T>
class UnknownBase : virtual public Unknown
{
protected:
inline UnknownBase() {} // Protected constructor
virtual ~UnknownBase() {} // Protected destructor

public:
static T * Create();
};







And then :


// Derived.hpp
class Derived : public UnknownBase<Derived>
{
protected:
virtual void selfDestroy();
};

// Derived.cpp
Derived * UnknownBase<Derived>::Create() { return new Derived(); }
void Derived::selfDestroy() { delete this; }




This solves the problems of :
- Mandatory allocation via a common static function (must derive from UnknownBase because Unknown's constructor is private)
- Deallocation via a virtual function
- Reference counting in base class
- Mandatory virtual inheritance from Unknown because of mandatory inheritance from UnknownBase

Is there any problem with this approach?

[Edited by - Trillian on May 12, 2007 7:04:26 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by ldeej
No, it would not work if you inherit from Unknown from different parents, the call will be ambiguous.

EDIT: Unless you use virtual inheritance for Unknown obviously :-)

I was taking the requirement for virtual inheritance as read.

Jans.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this