17

I'm using an exe which dynamically loads a DLL. A function in the DLL allocates memory on the heap and passes a pointer to that memory to the exe.

A senior says that it is bad practice to do so. He says that if I ever have to share memory between the exe and the DLL, the exe has to allocate memory and pass a pointer to that to the DLL, and not vice versa. Is this true? Why?

EDIT: In my case, I planned to allocate and deallocate memory inside the DLL itself.

Nav
  • 19,885
  • 27
  • 92
  • 135
  • I've never heard of that before. I was under the impression that the application heap belonged to the current running process; allocating objects on the heap would work the same way regardless of whether it was a DLL or executable making the call. But perhaps I am wrong. – Rook Nov 29 '12 at 12:07
  • 4
    Note that there are two classes of answer here: "tidy ownership semantics" and "CRT linking issues". Neither of those answers *necessarily* dictates that it's the exe that should allocate/free rather than the dll, but for the linking issue it's irrelevant which does it provided they're the same, whereas someone's idea of a good interface might be that the caller allocates (and it's irrelevant that the callee's in a dll). So, I think you need to further question the senior (and I think the senior needs to think about whether it's a good teaching technique to hand down rules without reasons). – Steve Jessop Nov 29 '12 at 13:14
  • The c++ stdlib (a dll in your terms) does allocate memory (for example in `get_temporary_buffer()`) and returns it to the caller (exe). – Walter Nov 29 '12 at 14:37
  • @Steve: Showed the senior the comments and answers here, and he says that logically, the DLL (he calls it a server) should not hold resources. The resources belong to the client, so the client should do the allocation. It's his idea of ownership of data. In my case, the DLL is using a third party library which needs the client to supply it certain specific information in a certain order, so the struct has to be created and allocated within the DLL itself. – Nav Nov 30 '12 at 03:52
  • Related: https://codereview.stackexchange.com/questions/153559/safely-handling-memory-allocation-across-dll-boundaries-in-windows and https://web.archive.org/web/20210516054945/https://devblogs.microsoft.com/oldnewthing/20060915-04/?p=29723 – Gabriel Devillers Mar 16 '22 at 09:29

10 Answers10

14

Here are some reasons for having the caller supply a pointer:

  1. Symmetric ownership semantics. This is already explained by several other answers.
  2. Avoids mismatching the allocator and deallocator. As mentioned in Aesthete's answer, if the DLL allocates a pointer and returns it, the caller must call the corresponding deallocator to free it. This is not necessarily trivial: the DLL might be statically linked against one version of, say, malloc/free while the .exe is linked against a different version of malloc/free. (For example, the DLL could be using release versions while the .exe is using specialized debug versions.)
  3. Flexibility. If the DLL is meant for general use, having the caller allocate the memory gives the caller more options. Suppose the caller doesn't want to use malloc and instead wants memory to be allocated from some specific memory pool. Maybe it's a case where the caller could provide a pointer to memory allocated on the stack. If the DLL allocated the memory itself, the caller does not have any of these options.

(The second and third points also mostly can be addressed by having the .exe supply an allocator/deallocator for the DLL code to use.)

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • I'm allocating and deallocating within the DLL itself, so allocation shouldn't be a problem. But I agree with point 3, about the caller having options of allocating into a specific memory pool. – Nav Nov 30 '12 at 03:15
  • @Nav note that flexibility comes here with a price of making caller's life a little bit harder. And it's open question what is better, because in opposite scenario **you** are able to optimize **your** DLL to use memory pools, whatever. And library user may not have a clue about your library internals, so upon which principle shall he decide how to allocate memory? Complex libraries would be unusuable if user had to allocate every structure for them. Many serious libraries, like `Qt` for example, manage memory on their own and this is completely fine. – mip Jan 26 '13 at 18:52
  • It seemed like providing an allocator function pointer would avoid all of the mentioned technical issues, but I rarely see it mentioned as a possible solution. It’s good to know that I am not missing anything. Sometimes computing the correct allocation size is potentially complicated and/or incremental, but the caller would prefer not to have to use a special deallocator for the returned buffer—something like glibc/BSD asprintf() is a good example. – musicinmybrain Mar 20 '15 at 12:40
13

One of the basic idea behind the design patterns is ownership. The idea is - one who creates a resource (and thereby holds it in the pointer) should be responsible for deleting the resource. This will ensure the sanctity of the design and in longer life of the projects, its developer can see lesser bugs.

So now it in your case, the DLL can be attached by any executable and he can try to delete the resource, which may cause future problem. So I think it has been suggested for vice-versa and I would say it as a sound advice.

kumar_m_kiran
  • 3,982
  • 4
  • 47
  • 72
  • 2
    While the ownership aspect is a nice principle, this answer kind of ignores the actual technical reason that the .exe and the .dll might use different allocators. For example, imagine that you're using a release build of a .dll with a debug version of an .exe that uses some special, debug version of `malloc` and `free`. – jamesdlin Nov 29 '12 at 12:42
  • 3
    With the Microsoft crt memory allocations / deallocations are incompatible from debug to release. You can not safely allocate memory in a debug dll and free it in a release exe or viseversa. – drescherjm Nov 29 '12 at 13:23
7

I have seen this issue before, and it is caused by the DLL and exe linking differently to the CRT (static, dynamic MT etc).

I you're going to pass a pointer to memory between DLL and executable, they should both provide some sort of Free() functionality to free memory from their respective heaps.

Aesthete
  • 18,622
  • 6
  • 36
  • 45
6

Generally, the heap (The One Heap) belongs to the process, and it does not matter where you allocate from, so this will work just fine, except when it doesn't.

Therefore, the claim that it is "bad practice" is valid. There are few things that are worse than something that works fine, except when it doesn't.

The worst part about it is that when everything blows up, it's not immediately obvious what's wrong, and you can easily run into an issue without knowing. It may be something as easy as linking the a particular version of the CRT into your DLL. Or someone in your team might have created a separate heap for some reason. Or, some other reason that isn't immediately obvious which caused another heap being created.

What makes the free-from-wrong-heap situation so vicious is that you don't generally know what will happen, or when (or if someone will notice).

You may get a NULL return value from a heap function or an exception. Your application might be prepared for either of them or it might not be (be honest, you always check return values, don't you?). It might crash immediately upon freeing, or you might just silently leak memory and run out of address space (or memory) minutes or hours later, and nobody will know why. Or, something else.
Thus, what you see may not at all be in (apparent) correlation to what caused the problem.

Damon
  • 67,688
  • 20
  • 135
  • 185
5

I only want to point out that passing an allocator to the DLL to allocate memory is perfectly safe and is the standard model used by the C++ std library itself. In this case, the allocation is done by the DLL via a method passed from the caller, avoiding to pass pointers, and avoiding the issue of linking to different implementations of malloc().

Walter
  • 44,150
  • 20
  • 113
  • 196
3

the exe and the dll may have different heaps. when either try to free memory allocated by the other the free fails and there is a leak.

only if both, the exe and the dll use CRT dynamically with the same version of the CRT they use the same heap.

so it is a very good advice to do allocation and free in the same binary.

stefan
  • 3,681
  • 15
  • 25
  • i want to make this more explicit: NEVER EVER try to allocate in an exe and free in a dll or vice versa. the heap does not belong to the application or process. it belongs to the CRT. ONLY if both use the very same version of CRT dynamically it works. but this is very thin ice. so nerver ever try it. – stefan Nov 29 '12 at 12:58
2

I'd argue that it is generally bad practice to hand around raw pointers. In case of a dll, it should return a smart-pointer that has a custom deleter that appropriately handles the cleanup (options are std::unique_ptr, std::shared_ptr or boost::shared_ptr in order of preference).

In fact, it is probably safer to use std::weak_ptr or boost::weak_ptr - these require you to check each time you want to access the resource, because the dll might have been unloaded in the meantime.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • 3
    This is fine if your platform's ABI specifies the implementation of the shared pointer in question. If it doesn't, then you can't use them across dll boundaries at all (or rather, you can arrange to be able to do so but you must first tie down enough implementation details on both sides of the call to ensure that they match). That aside, the advice applies equally to all function calls, the fact that this one's in a dll is irrelevant, and it's good advice. But if the problem is mismatched CRTs, then this advice makes the problem worse, not better. – Steve Jessop Nov 29 '12 at 13:20
  • Oh, except that once the CRT issue is overcome my order of preference would be `unique_ptr`, `auto_ptr`, `shared_ptr`. `auto_ptr` is hard to use and is superseded by `unique_ptr`, but the one thing it does well is signal ownership of return values in C++03. So if you want to cover the overwhelming likelihood that the questioner is using C++03 rather than C++11 it's worth a mention ;-) – Steve Jessop Nov 29 '12 at 13:26
  • All very good points. I got confused there. I used to wrap pointers I got from dlls in smart-pointers myself, and make sure the correct release-method is called. This circumvents the CRT issue, but still places the responsibility for correct resource-management on the client. – Björn Pollex Nov 29 '12 at 13:29
  • Agreed, and one trick is for the dll author to provide a header file with functions in an anonymous namespace that does that wrapping on the caller side of the boundary, but using code that the dll author maintains. If ownership genuinely is shared between exe and dll then it gets a bit trickier. I don't remember ever doing that case myself, but I suppose you could have a shared_ptr on the exe side whose deleter destroys a dll-side shared_ptr. – Steve Jessop Nov 29 '12 at 13:31
  • 1
    While the first part of the answer has been discussed already - I strongly disagree with the second part, the weak pointer. The reference-counting-object resides in the same heap as the object itself. If the objects memory is somehow gone, the reference counting object is gone as well and the weak pointer will access invalid memory when trying to convert to a shared pointer. A weak pointer will not provide additional safety. – stefan Oct 02 '15 at 15:59
2

As @kumar_m_kiran already pointed out, it's about ownership issue, however I want to note, that @aleguna is correct too, therefore, imho, correct rule is "Either allocate and deallocate same memory in DLL or in EXE, but not in both".

hate-engine
  • 2,300
  • 18
  • 26
0

It's not necessarily bad practice but it's dangerous practice (and probably bad). You need to carefully think about who is responsible for freeing the memory. The exe will probably not be able to (or should not be able to) free the DLL's memory directly itself so presumably will be passing this pointer back to the DLL at some later date. So now we're passing a pointer back and forth between an EXE and a DLL which isn't nice.

TheMathemagician
  • 968
  • 9
  • 21
0

I'd say no, it's not "bad practice". From my experience, you just need to be careful that you release that pointer in the right memory space. I've built a graphics engine that allocated assets in multiple DLLs (each DLL represented a mini game); using a shared_ptr (at the time it was Boost, however, I'm sure the C++11 (or newer) std::shared_ptr supports the same semantics). I would supply a function that would delete the memory in the right space. The main concern, at this point, is that you ensure you free your shared_ptrs before you free the DLL. I can't recall now, but we might have used a list of shared_ptrs owned by the DLL/DLL wrapper, and all other uses of the pointer were through a weak_ptr TO that shared_ptr.

Kit10
  • 1,345
  • 11
  • 12