Opened 13 years ago

Closed 11 years ago

#3658 closed Feature Requests (invalid)

Remove dependency on static initialization/destruction order

Reported by: Andrey Semashev Owned by: Joaquín M López Muñoz
Milestone: Boost 1.42.0 Component: flyweight
Version: Boost 1.41.0 Severity: Problem
Keywords: Cc:

Description

The static_holder depends on the static initialization/destruction order, as it is implemented as a function-local static variable. If there are flyweights in the static or global variables, which are initialized lazilly, the flyweight value may get destroyed before the flyweights that refer to it. For example:

#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/flyweight.hpp>
#include <boost/flyweight/key_value.hpp>

struct A
{
	int m_n;

	A(int n) : m_n(n)
	{
		std::cout << "A()" << std::endl;
	}
	~A()
	{
		std::cout << "~A()" << std::endl;
	}

	bool operator== (A const& a) const
	{
		return m_n == a.m_n;
	}

	friend std::size_t hash_value(A const& a)
	{
		return a.m_n;
	}
};

struct B
{
	boost::flyweight< boost::flyweights::key_value< int, A > > m_A;

	B() : m_A(10)
	{
		std::cout << "B()" << std::endl;
	}
	~B()
	{
		std::cout << "~B()" << std::endl;
	}
};

boost::shared_ptr< B > p;

int main(int, char*[])
{
	p.reset(new B());
	return 0;
}

Compiled with MSVC:

cl -Ox -MD -EHsc -I . -DNDEBUG ./flyweight_test.cpp

Produces:

A()
B()
~A()
~B()

And a crash.

A possible solution would be to destroy flyweight values only when reference counter from flyweights drops to zero.

Change History (5)

comment:1 by Joaquín M López Muñoz, 13 years ago

Resolution: invalid
Status: newclosed

The issue is a known limitation of static_holder that can't be dealt with in a more satisfactory manner than its current implementation. The solution requires some user help, as explained in

http://www.boost.org/libs/flyweight/doc/tutorial/technical.html#static_init

In your particular case, add the following before the line where p is declared:

static boost::flyweight< boost::flyweights::key_value< int, A > >::initializer fwinit;
boost::shared_ptr< B > p;

comment:2 by Andrey Semashev, 13 years ago

Resolution: invalid
Status: closedreopened

That solution is not suitable for me because p and B and A are rather distant (they belong to different components of the application). In fact, the component that creates p doesn't know anything about A's implementation, and about flyweights in particular.

Why is it not possible to implement a fair reference counted semantics?

I reopen the ticket as I feel that this problem is not solved yet. This may not be a coding bug, but I believe this is a major design flaw that deserves to be addressed. If you have any objections or suggestions, we may discuss it either here or on the dev. ML.

comment:3 by Joaquín M López Muñoz, 13 years ago

Yes, please bring this to the mailing list so that we can discuss it in a more fluent manner. Thank you!

comment:4 by Andrey Semashev, 13 years ago

comment:5 by Joaquín M López Muñoz, 11 years ago

Resolution: invalid
Status: reopenedclosed

I'm re-closing this old one, as the suggestions on the mailing list discussion seem to me too cumbersome to implement, and basically break the orthogonality of the different concepts (factory, holder, values.) As suggested there, you can define your own holder that basically does not delete the factory, at the cost of a memory leak (if it can be called a leak, as this is not your normal leak where memory usage increases over time, which is the problematic case.)

Note: See TracTickets for help on using tickets.