Search code examples
c++singletonapache-phoenix

How we place Phoenix Singleton on same address? C++


below code illustrates the Phoenix Singleton described in Andrey Alexandrescu's Modern C++ Design book.

Singleton& Instance()
{
   if (!pInstance_)
   {
   // Check for dead reference
      if (destroyed_)
      {
         OnDeadReference();
      }
      else
      {
   // First call—initialize
         Create();
      }
   }
return pInstance_;
}

void Singleton::OnDeadReference()
{
// Obtain the shell of the destroyed singleton
   Create();
// Now pInstance_ points to the "ashes" of the singleton
// - the raw memory that the singleton was seated in.
// Create a new singleton at that address
   new(pInstance_) Singleton;
// Queue this new object's destruction
   atexit(KillPhoenixSingleton);
// Reset destroyed_ because we're back in business
   destroyed_ = false;
}

static void Create();
{

// Task: initialize pInstance_
   static Singleton theInstance;
   pInstance_ = &theInstance;
}

void Singleton::KillPhoenixSingleton()
{
// Make all ashes again
// - call the destructor by hand.
// It will set pInstance_ to zero and destroyed_ to true
   pInstance_->~Singleton();
}


virtual ~Singleton()
{
   pInstance_ = 0;
   destroyed_ = true;
}

Singleton* Singleton::pInstance_ = 0;
bool Singleton::destroyed_ = false;

Quote from the book:

Let's analyze the flow of events. During the application exit sequence, Singleton's destructor is called. The destructor resets the pointer to zero and sets destroyed_ to true. Now assume some global object tries to access Singleton again. Instance calls OnDeadReference. OnDeadReference reanimates Singleton and queues a call to KillPhoenixSingleton, and Instance successfully returns a reference to a valid Singleton object. From now on, the cycle may repeat.

My question is - if we assign to pointer pInstance_ = 0 in the destructor, than it assigns to a local static reference which should be deleted by that point, how that we place a new object at 0 address? Please tell me that I am missing something. If someone can explain me the flow, I would really appreciate. Thanks


Solution

  • The "Phoenix" name comes from the legendary bird that died, caught fire and was born again from its ashes.

    In this case, yes, the Singleton can die, the destructor runs, pInstance becomes nullptr, and it's gone - until it needs to revive. When that happens, Create makes pInstance non-null before new (pInstance) Singeton recreates the object.

    [edit] You need to know the rules for object lifetimes and storage duration. Obviously, the storage for an object must be allocated before an object is created, and the storage must not be deallocated until the object is destroyed. The interval in between is known as the storage duration. Now, there is such a thing as static storage duration. Such storage isn't reclaimed until the program exits, even if the object in that storage is destroyed. Variables defined at namespace scope ("globals") and static variables have this static storage duration.

    Therefore, the storage for theSingleton has static storage duration, and pInstance can point to this storage even if the object there is destroyed. Note that the destroyed and pInstance variables themselves also have static storage duration, as they're globals.