Boost logo

Boost :

From: Vahan Margaryan (vahan_at_[hidden])
Date: 2002-05-22 10:28:22

Hello Robert,
Have you considered solving the exception-safety implications of the pointer
serialization mechanism? There is a problem when an exception occurs during
loading of objects that are pointed to from several locations.

The problem.
Consider this, I have a class A that holds a single pointer to another
object of class A:

class A{ ...
 A* other_;

A doesn't own that pointer - it is passed to it via constructor and is not
deleted in the destructor.

Then I have a class AManager. AManager holds a vector of A*. In one of the
member functions, AManager creates 100 A objects, links them via their
other_ members and pushes them into the vector. In the destructor, AManager
destroys the elements of the vector (thus, it owns them).

What happens when I restore an object of the type AManager and something
wrong happens? It goes like this:
1. AManager tries to load a vector. It tries to read in the first element of
the vector.
2. Loading the first element means loading the other_ member of the first
3. Since all the A objects are linked together, the process loads all the
other A objects too, while still in the context of loading the first object.
All the A objects are allocated.
4. Then the loading function of the first object fails with an exception
(corrupted file, end-of-file, or something else).
5. AManager attempts to destroy the loaded objects by destroying the
elements of the vector. But the vector contains no elements.
6. All the objects allocated while loading AManager are left in memory,
memory leaks result.

A possible solution.
Generally speaking, there's no quick fix for this, AFAIK. The way I solved
it was by indicating whether the pointer was owned by the object.
When an object owns its member pointer, it says so to the loading function:

load(ar, ptr, owns( )); //owns is a type

When the object doesn't own its member pointer, it just says:

load(ar, ptr);

(I use overloading, because the ownership status is usually known at compile

Now the lookup table that stores pointers that have been new-ed (to check
for multiple references to the same object) also holds a boolean value for
each pointer, that indicates whether the ownership of the given pointer has
been taken.

When an exception occurs, the system iterates over the items in the lookup
table and deletes all the items that don't have the boolean value set (the
others will be erased by their owning objects).

Container issues.
The situation is slightly more complicated for standard containers. Given a
vector<A*>, should the system assume the vector owns its elements or not?
There's no universal answer. Therefore, when loading/saving containers, the
user needs to specify the ownership (except for the non-owning default).
Containers can be several levels deep, like in vector<vector<A*> * >, so the
ownership indicator can be fairly complex.

I would suggest using a typelist as a parameter to load, with elements
owns( ) and noown( ). At each level, the loading function pops off the
topmost type and based on that, performs either owning or non-owning load.
The tail of the typelist is passed further.

The function load(T) calls load(T, noown( ) ).

Aside from this, the container loading functions must be modified to make
sure the currently read object isn't leaked. Thus, instead of:

for( int i = 0; i < size; ++i)
    T t;
    load(ar, t, own);

the function should go:

for(int i = 0; i < size; ++i)
    vector.push_back(T( ));
    load(ar, vector.back( ), own);

For set/hashset/map/hashmap this is not possible, so a try/catch is needed.

Since passing extra parameters is needed, the >> operator isn't convenient,
so I used load.

Unfortunately, I can't submit any real code implementing the approach (for
legal reasons), but it has been implemented and it works.

Hope that reading this loooooong mail wasn't wasted time :)

Boost list run by bdawes at, gregod at, cpdaniel at, john at