libfoedus-core
FOEDUS Core Library
Initialize/Uninitialize Resources

Defines a uniform class interface to initialize/uninitialize non-trivial resources. More...

Detailed Description

Defines a uniform class interface to initialize/uninitialize non-trivial resources.

Constructor/Destructor vs initialize()/uninitialize()
Constructor should not do complicated initialization as it can't return errors. Instead, we universally use initialize()/uninitialize() semantics for all long-living objects. By long-living, we mean at least seconds. Small and short-living objects do not have to follow this semantics as it might cause unnecessary complexity or overhead.

Same applies to Destructor vs uninitialize(). Furthermore, C++ doesn't allow calling virtual functions (uninitialize()) in destructor as the class information was already torn down! So, make sure you always explicitly call uninitialize(). If you didn't call uninitialize(), you actually leak the resource.

DefaultInitializable
For most classes, you can use DefaultInitializable class to save repetitive code. For example, declare your class like the following:
class YourClass : public DefaultInitializable {
public:
YourClass(const ConfigurationForYourClass &conf, int other_params, ...);
...
ErrorStack initialize_once() CXX11_OVERRIDE;
ErrorStack uninitialize_once() CXX11_OVERRIDE;
};
Then, define initialize_once()/uninitialize_once() as follows in cpp.
ErrorStack YourClass::initialize_once() {
.... // one-time initialization for this object
return kRetOk;
}
ErrorStack YourClass::uninitialize_once() {
// one-time uninitialization for this object. continue releasing resources even when
// there is some error. So, use ErrorStackBatch to wrap more than one errors.
ErrorStackBatch batch;
batch.emplace_back(some_uninitialization());
batch.emplace_back(another_uninitialization());
batch.emplace_back(yet_another_uninitialization());
return SUMMARIZE_ERROR_BATCH(batch);
}
UninitializeGuard, or how RAII fails in real world.
The code that instantiates an Initializable object must call uninitialize() for the reasons above. Even if there is some error, which causes an early return from the function by CHECK_ERROR() in many cases. Note that try-catch(...) does not work because this is NOT an exception (and we never use exceptions in our code). YOU have to make sure uninitialize() is called. UninitializeGuard addresses this issue, but imperfectly. For example, use it like this:
ErrorStack your_func() {
YourInitializable object;
CHECK_ERROR(object.initialize());
{
UninitializeGuard guard(&object, UninitializeGuard::kWarnIfUninitializeError);
CHECK_ERROR(object.do_something());
CHECK_ERROR(object.uninitialize());
}
}
The UninitializeGuard object automatically calls object.uninitialize() when do_something() fails and we return from your_func(). HOWEVER, C++'s destructor can't propagate any errors, in our case ErrorStack. The second argument (UninitializeGuard::kWarnIfUninitializeError) says that if it encounters an error while uninitialize() call, it just warns it in debug log. Logging the error is not a perfect solution at all. So, this is just an imperfect safety net. Unfortunately, this is all what we can do in C++ for objects that have non-trivial release.
Collaboration diagram for Initialize/Uninitialize Resources:

Classes

class  foedus::Initializable
 The pure-virtual interface to initialize/uninitialize non-trivial resources. More...
 
class  foedus::DefaultInitializable
 Typical implementation of Initializable as a skeleton base class. More...
 
class  foedus::UninitializeGuard
 Calls Initializable::uninitialize() automatically when it gets out of scope. More...