Generic StaticObject, any static var can be thread safe!

This is a very simple template class that wraps your object so that it can exist with static storage duration and be completely immune to the following problems:

  1. Order of construction problems for objects with static storage duration and file scope

  2. Thread safety problems for objects with static storage duration and function scope

This has been talked about in other threads in the forum, and in my effort to make this as painless as possible, easy to use, and perfect in it’s design this is what we have so far:

NOT THREAD SAFE

    static RunningThreadsList& getInstance()
    {
        static RunningThreadsList runningThreads;
        return runningThreads;
    }

To fix this using StaticObject, all you need to do is include the template and change ONE LINE !!!

FIXED, THREAD SAFE

    static RunningThreadsList& getInstance()
    {
        static StaticObject <RunningThreadsList> runningThreads; // fixed!
        return runningThreads;
    }

Here’s the code:

// Copyright (C) 2008-2011 by Vincent Falco, All rights reserved worldwide.
// This file is released under the MIT License:
// http://www.opensource.org/licenses/mit-license.php
//
// Wraps an object with a thread-safe initialization preamble so that it can
// properly exist with static storage duration.
//
// Implementation notes:
//
//   This is accomplished by omitting the constructor and relying on the C++
//   specification that plain data types with static storage duration are filled
//   with zeroes before any other initialization code executes.
//
// Spec: N2914=09-0104
//
// [3.6.2] Initialization of non-local objects
//
//         Objects with static storage duration (3.7.1) or thread storage
//         duration (3.7.2) shall be zero-initialized (8.5) before any
//         other initialization takes place.
//
// Requirements:
//
//  Object must be constructible without parameters.
//  The StaticObject must be declared with static storage duration or
//    the behavior is undefined.
//
// Usage example:
//
// Object* getInstance ()
// {
//   static StaticObject <Object> instance;
//   return instance->getObject ();
// }
//
template <class Object>
class StaticObject
{
public:
  // constructor intentionally omitted to ensure correct behavior

  ~StaticObject ()
  {
    if (getConstructedFlag().get () != 0)
    {
      getObjectPtr()->~Object();
    }
  }

  operator Object& ()
  {
    return *getObject();
  }

  Object* getObject ()
  {
    if (getConstructedFlag().get () == 0)
    {
      if (getInitializingFlag().compareAndSetBool (1, 0))
      {
        new (getObjectPtr()) Object;

        getConstructedFlag().set (1);
      }
      else
      {
        do
        {
          // spin until constructed
          Thread::yield ();
        }
        while (getConstructedFlag().get() == 0);
      }
    }

    return getObjectPtr();
  }

  inline Object* operator-> ()
  {
    return getObject();
  }

  inline Object& operator* ()
  {
    return *getObject();
  }

private:
  inline Object* getObjectPtr ()
  {
    return reinterpret_cast <Object*> (object);
  }

  inline Atomic <int>& getConstructedFlag ()
  {
    return *(reinterpret_cast <Atomic <int>*> (constructed));
  }

  inline Atomic <int>& getInitializingFlag ()
  {
    return *(reinterpret_cast <Atomic <int>*> (initializing));
  }

private:
  // Need manual alignment
  #if JUCE_64BIT
    JUCE_ALIGN (8)
  #else
    JUCE_ALIGN (4)
  #endif
  char constructed [sizeof (Atomic <int>)];

  #if JUCE_64BIT
    JUCE_ALIGN (8)
  #else
    JUCE_ALIGN (4)
  #endif
  char initializing [sizeof (Atomic <int>)];

  #if JUCE_64BIT
    JUCE_ALIGN (8)
  #else
    JUCE_ALIGN (4)
  #endif
  char object [sizeof (Object)];
};

This replaces my StaticCriticalSection since it is more generic. You can still get a StaticCriticalSection if you want, just do this:

typedef StaticObject <CriticalSection> StaticCriticalSection;

Then use the StaticCriticalSection in your code as before.

This implementation does not add any critical section or heavy weight synchronization. The only added burden is ONE memory barrier / atomic read in the fast path (“if (getConstructedFlag().get () == 0)” is almost always false.

The template solves two problems, one of which can cause defects in several places in Juce. Using it to fix an object with function scope is as easy as changing one declaration!

Seriously, how can anyone resist this?

1 Like