/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#if !defined INCLUDED_RTL_INSTANCE_HXX
#define INCLUDED_RTL_INSTANCE_HXX

#include "osl/doublecheckedlocking.h"
#include "osl/getglobalmutex.hxx"

namespace {

/** A non-broken version of the double-checked locking pattern.

    See
    <http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html>
    for a description of double-checked locking, why it is broken, and how it
    can be fixed.  Always use this template instead of spelling out the
    double-checked locking pattern explicitly, and only in those rare cases
    where that is not possible and you have to spell it out explicitly, at
    least call OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER() at the right
    places.  That way, all platform-dependent code to make double-checked
    locking work can be kept in one place.

    Usage scenarios:

    1  Static instance (most common case)

    Pattern:

      T * getInstance()
      {
          static T * pInstance = 0;
          if (!pInstance)
          {
              ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
              if (!pInstance)
              {
                  static T aInstance;
                  pInstance = &aInstance;
              }
          }
          return pInstance;
      }

    Code:

      #include "rtl/instance.hxx"
      #include "osl/getglobalmutex.hxx"

      namespace {
          struct Init
          {
              T * operator()()
              {
                  static T aInstance;
                  return &aInstance;
              }
          };
      }

      T * getInstance()
      {
          return rtl_Instance< T, Init, ::osl::MutexGuard,
                               ::osl::GetGlobalMutex >::create(
              Init(), ::osl::GetGlobalMutex());
      }

    2  Dynamic instance

    Pattern:

      T * getInstance()
      {
          static T * pInstance = 0;
          if (!pInstance)
          {
              ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
              if (!pInstance)
                  pInstance = new T;
          }
          return pInstance;
      }

    Code:

      #include "rtl/instance.hxx"
      #include "osl/getglobalmutex.hxx"

      namespace {
          struct Init
          {
              T * operator()()
              {
                  return new T;
              }
          };
      }

      T * getInstance()
      {
          return rtl_Instance< T, Init, ::osl::MutexGuard,
                               ::osl::GetGlobalMutex >::create(
              Init(), ::osl::GetGlobalMutex());
      }

    3  Other guard/mutex

    Pattern:

      T * getInstance()
      {
          static T * pInstance = 0;
          if (!pInstance)
          {
              SomeGuard aGuard(pSomeMutex);
              if (!pInstance)
              {
                  static T aInstance;
                  pInstance = &aInstance;
              }
          }
          return pInstance;
      }

    Code:

      #include "rtl/instance.hxx"

      namespace {
          struct InitInstance
          {
              T * operator()()
              {
                  static T aInstance;
                  return &aInstance;
              }
          };

          struct InitGuard
          {
              SomeMutex * operator()()
              {
                  return pSomeMutex;
              }
          };
      }

      T * getInstance()
      {
          return rtl_Instance< T, InitInstance,
                               SomeGuard, InitGuard >::create(
              InitInstance(), InitMutex());
      }

    4  Calculate extra data

    Pattern:

      T * getInstance()
      {
          static T * pInstance = 0;
          if (!pInstance)
          {
              Data aData(...);
              ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
              if (!pInstance)
              {
                  static T aInstance(aData);
                  pInstance = &aInstance;
              }
          }
          return pInstance;
      }

    Code:

      #include "rtl/instance.hxx"
      #include "osl/getglobalmutex.hxx"

      namespace {
          struct InitInstance
          {
              T * operator()()
              {
                  static T aInstance;
                  return &aInstance;
              }
          }

          struct InitData
          {
              Data const & operator()()
              {
                  return ...;
              }
          }
      }

      T * getInstance()
      {
          return rtl_Instance< T, InitInstance,
                               ::osl::Mutex, ::osl::GetGlobalMutex,
                               Data, InitData >::create(
              InitInstance(), ::osl::GetGlobalMutex(), InitData());
      }

    Some comments:

    For any instantiation of rtl_Instance, at most one call to a create method
    may occur in the program code:  Each occurance of a create method within
    the program code is supposed to return a fresh object instance on the
    first call, and that same object instance on subsequent calls; but
    independent occurances of create methods are supposed to return
    independent object instances.  Since there is a one-to-one correspondence
    between object instances and instantiations of rtl_Instance, the
    requirement should be clear.  One measure to enforce the requirement is
    that rtl_Instance lives in an unnamed namespace, so that instantiations of
    rtl_Instance in different translation units will definitely be different
    instantiations.  A drawback of that measure is that the name of the class
    needs a funny "hand coded" prefix "rtl_" instead of a proper namespace
    prefix like "::rtl::".

    A known problem with this template is when two occurences of calls to
    create methods with identical template arguments appear in one translation
    unit.  Those two places will share a single object instance.  This can be
    avoided by using different Init structs (see the above code samples) in
    the two places.

    There is no need to make m_pInstance volatile, in order to avoid usage of
    stale copies of m_pInstance:  At the first check, a thread will see that
    m_pInstance contains either 0 or a valid pointer.  If it contains a valid
    pointer, it cannot be stale, and that pointer is used.  If it contains 0,
    acquiring the mutex will ensure that the second check sees a non-stale
    value in all cases.

    On some compilers, the create methods would not be inlined if they
    contained any static variables, so m_pInstance is made a class member
    instead (and the create methods are inlined).  But on MSC, the definition
    of the class member m_pInstance would cause compilation to fail with an
    internal compiler error.  Since MSC is able to inline methods containing
    static variables, m_pInstance is moved into the methods there.  Note that
    this only works well because for any instantiation of rtl_Instance at most
    one call to a create method should be present, anyway.
 */
template< typename Inst, typename InstCtor,
          typename Guard, typename GuardCtor,
          typename Data = int, typename DataCtor = int >
class rtl_Instance
{
public:
    static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor)
    {
#if defined _MSC_VER
        static Inst * m_pInstance = 0;
#endif // _MSC_VER
        Inst * p = m_pInstance;
        if (!p)
        {
            Guard aGuard(aGuardCtor());
            p = m_pInstance;
            if (!p)
            {
                p = aInstCtor();
                OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
                m_pInstance = p;
            }
        }
        else
        {
            OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
        }
        return p;
    }

    static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor,
                                DataCtor aDataCtor)
    {
#if defined _MSC_VER
        static Inst * m_pInstance = 0;
#endif // _MSC_VER
        Inst * p = m_pInstance;
        if (!p)
        {
            Data aData(aDataCtor());
            Guard aGuard(aGuardCtor());
            p = m_pInstance;
            if (!p)
            {
                p = aInstCtor(aData);
                OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
                m_pInstance = p;
            }
        }
        else
        {
            OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
        }
        return p;
    }

private:
#if !defined _MSC_VER
    static Inst * m_pInstance;
#endif // _MSC_VER
};

#if !defined _MSC_VER
template< typename Inst, typename InstCtor,
          typename Guard, typename GuardCtor,
          typename Data, typename DataCtor >
Inst *
rtl_Instance< Inst, InstCtor, Guard, GuardCtor, Data, DataCtor >::m_pInstance
= 0;
#endif // _MSC_VER

}

namespace rtl {

/** Helper base class for a late-initialized (default-constructed)
    static variable, implementing the double-checked locking pattern correctly.
    
    @derive
    Derive from this class (common practice), e.g.
    <pre>
    struct MyStatic : public rtl::Static<MyType, MyStatic> {};
    ...
    MyType & rStatic = MyStatic::get();
    ...
    </pre>
    
    @tplparam T
              variable's type
    @tplparam Unique
              Implementation trick to make the inner static holder unique,
              using the outer class
              (the one that derives from this base class)
*/
template<typename T, typename Unique>
class Static {
public:
    /** Gets the static.  Mutual exclusion is performed using the
        osl global mutex.
        
        @return
                static variable
    */
    static T & get() {
        return *rtl_Instance<
            T, StaticInstance,
            ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
                StaticInstance(), ::osl::GetGlobalMutex() );
    }
private:
    struct StaticInstance {
        T * operator () () {
            static T instance;
            return &instance;
        }
    };
};

/** Helper class for a late-initialized static aggregate, e.g. an array,
    implementing the double-checked locking pattern correctly.
    
    @tplparam T
              aggregate's element type
    @tplparam InitAggregate
              initializer functor class
*/
template<typename T, typename InitAggregate>
class StaticAggregate {
public:
    /** Gets the static aggregate, late-initializing.
        Mutual exclusion is performed using the osl global mutex.
        
        @return
                aggregate
    */
    static T * get() {
        return rtl_Instance<
            T, InitAggregate,
            ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
                InitAggregate(), ::osl::GetGlobalMutex() );
    }
};

/** Helper base class for a late-initialized static variable,
    implementing the double-checked locking pattern correctly.
    
    @derive
    Derive from this class (common practice),
    providing an initializer functor class, e.g.
    <pre>
    struct MyStatic : public rtl::StaticWithInit<MyType, MyStatic> {
        MyType operator () () {
            ...
            return MyType( ... );
        }
    };
    ...
    MyType & rStatic = MyStatic::get();
    ...
    </pre>
    
    @tplparam T
              variable's type
    @tplparam InitData
              initializer functor class
    @tplparam Unique
              Implementation trick to make the inner static holder unique,
              using the outer class
              (the one that derives from this base class).
              Default is InitData (common practice).
    @tplparam Data
              Initializer functor's return type.
              Default is T (common practice).
*/
template<typename T, typename InitData,
         typename Unique = InitData, typename Data = T>
class StaticWithInit {
public:
    /** Gets the static.  Mutual exclusion is performed using the
        osl global mutex.
        
        @return
                static variable
    */
    static T & get() {
        return *rtl_Instance<
            T, StaticInstanceWithInit,
            ::osl::MutexGuard, ::osl::GetGlobalMutex,
            Data, InitData >::create( StaticInstanceWithInit(),
                                      ::osl::GetGlobalMutex(),
                                      InitData() );
    }
private:
    struct StaticInstanceWithInit {
        T * operator () ( Data d ) {
            static T instance(d);
            return &instance;
        }
    };
};

} // namespace rtl

#endif // INCLUDED_RTL_INSTANCE_HXX
