Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 9.  Dynamic Libraries


9.5. Resource Ownership

Good resource consumers should always return a resource whence it came. In general, if fn1() in link unit A allocates a resource, and returns it to its caller fn2() in link unit B, then fn2() should return that resource to a function in link unit A. If it does not, then it is likely that the program will fail.

This issue is of particular importance when dealing with dynamic libraries, since any static resource manager objects will be local to the dynamic library's link space. Thus, calling delete on some memory in one library that was allocated in another can be as nonsensical as returning an un-liked birthday present from your mother-in-law to your next-door neighbor.[3]

[3] Unless your mother-in-law is your next-door neighbor, that is. But this is a tale of dynamic linking, not a horror story!

There are two solutions to this problem.

9.5.1 Shared Pools

The first is to ensure that all the dynamic libraries used by the executable use a shared pool in a single dynamic library. There are two ways of doing this, but both boil down to the same thing. Let's consider the issue of memory.

One way to provide safe memory allocation and deallocation between dynamic libraries is to have all libraries themselves depend on a single dynamic library that provides operators new & delete and/or the functions malloc()/free(). The Visual C++ runtime library uses this model in the form of MSVCRT.DLL, and other vendors offer similar facilities.

A variant of this is to provide a per-link-unit implementation of these functions, but implement them in terms of a shared allocation function. This is the approach I use in the Synesis shared libraries on Win32, whereby there is a local new/delete that depend on functions (Mem_Alloc(), Mem_Free(), etc.) in the core dynamic library.

This is the most straightforward manner of sharing C++ implementations in your resource-handling dynamic libraries.

9.5.2 Return to Callee

The better general solution is always to return your resources to the callee who gave them to you, but it can be made difficult by the use of C++ classes. If your class's implementation is in one shared library, and all others share it, then it all works fine. But if some of the implementation is built into each link space—for example, by use of inline function definitions—then something constructed in one dynamic library and destroyed in another will be passing through different code, even though it's using the same class.

In practice, the use of classes obliges one to use the shared pools approach for most resources, especially the more ubiquitous and granular: if it's memory you're likely already using shared pools anyway courtesy of your compiler vendor. However, for other resources the pools may not be shared. If so, you could be in trouble if you're maintaining separate pools in separate dynamic libraries.


      Previous section   Next section