library of assembled shared sources

http://lass.cocamware.com

singleton_impl.cpp

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 
00044 
00045 #include "util_common.h"
00046 #include "singleton_impl.h"
00047 #include "../atomic.h"
00048 #include <cstdlib>
00049 #include <queue>
00050 
00051 namespace lass
00052 {
00053 namespace util
00054 {
00055 namespace impl
00056 {
00057 
00058 /** helper class to compare destruction priorities fo lass::util::SingletonBase.
00059  *  @internal
00060  *  @author Bram de Greve [Bramz]
00061  */
00062 class CompareDestructionPriority
00063 {
00064 public:
00065 
00066     bool operator()(SingletonBase* iA, SingletonBase* iB);
00067 };
00068 
00069 /** The singleton guard will take care of the destruction of all singletons.
00070  *  @internal
00071  *  @author Bram de Greve [Bramz]
00072  *
00073  *  All singletons will subscribe them to this guard with a DestructionPriority.  On an explicit
00074  *  call of killEmAll() (or on destruction of the guard), all singletons will be destructed.
00075  *  The singletons with the highest DestructionPriority will be killed first.  The order in which
00076  *  singletons of the same priority are destructed is undefined.
00077  *
00078  *  The system of using this destruction priority controlled by a guard that is destructed by
00079  *  @c ::atexit() , is inspired by Alexandrescu's @e longevity singletons [1].
00080  *
00081  *  @warning you should never call @c killEmAll() yourself!
00082  *
00083  *  @b Reference:
00084  *  -# ALEXANDRESCU A. (2001), <i>Modern C++ Design: Generic Programming and Design Patterns
00085  *     applied</i>, C++ in depth series, Addison-Wesley, pages 129-156
00086 */
00087 class SingletonGuard
00088 {
00089 public:
00090 
00091     ~SingletonGuard();
00092 
00093     void subscribe(SingletonBase* iSingleton);
00094 
00095     static SingletonGuard* instance();
00096 
00097 private:
00098 
00099     SingletonGuard() {}
00100 
00101     /** Special semaphore for the guard.
00102      *  @internal
00103      *  The reason we need a custom semaphore implemented around a static int is because
00104      *  the util::Semaphore is susceptible to the static initialization order fiasco.
00105      *  To solve that, we need something that can be initialized statically (baked in
00106      *  executable), and that would be - in this case - an int.  It will appear to have
00107      *  been one since the beginning of the ages.  Case solved.
00108      */
00109 #pragma LASS_TODO("Examine if util::Semaphore is indeed scusceptilbe to SIOF [Bramz]")
00110     class CustomSemaphore
00111     {
00112     public:
00113         CustomSemaphore()
00114         {
00115             int oldSlots, newSlots;
00116             do
00117             {
00118                 oldSlots = freeSlots_;
00119                 LASS_ASSERT(oldSlots >= 0);
00120                 newSlots = oldSlots - 1;
00121             }
00122             while (oldSlots == 0 || !atomicCompareAndSwap(freeSlots_, oldSlots, newSlots));
00123         }
00124         ~CustomSemaphore()
00125         {
00126             atomicIncrement(freeSlots_); 
00127         }
00128     private:
00129         static int freeSlots_;
00130     };
00131 
00132     typedef std::priority_queue
00133         <SingletonBase*, std::vector<SingletonBase*>, CompareDestructionPriority> TDeathRow;
00134 
00135     TDeathRow deathRow_;
00136 
00137     static void killEmAll();
00138     static bool deadReference(bool iSetReferenceToDead = false);
00139 
00140     static SingletonGuard* instance_;
00141     
00142 };
00143 
00144 SingletonGuard* SingletonGuard::instance_ = 0;
00145 int SingletonGuard::CustomSemaphore::freeSlots_ = 1;
00146 
00147 
00148 
00149 /** constructor.
00150  *  @warning The singleton base DOES NOT subsribes itself at construction.  Singleton<> must do this
00151  *           at the construction of a new singleton.  Why?  We better don't do it here, because
00152  *           the singleton is being constructed!  it might not be complete yet.
00153  */
00154 SingletonBase::SingletonBase():
00155     destructionPriority_(0)
00156 {
00157 }
00158 
00159 
00160 
00161 SingletonBase::~SingletonBase()
00162 {
00163 }
00164 
00165 
00166 
00167 int SingletonBase::destructionPriority() const
00168 {
00169     return destructionPriority_;
00170 }
00171 
00172 
00173 
00174 /** Subscribe to the singleton guard.
00175  *  Do this only once for each singleton, and in fact, don't do it yourself at all
00176  *  since Singleton<> already does it :)
00177  *
00178  *  @warning this isn't thread safe as it is, but that's ok because its only caller 
00179  *      Singleton::instance() is already locking on SingletonGuard level.
00180  */
00181 void SingletonBase::subscribeInstance(int iDestructionPriority)
00182 {
00183     destructionPriority_ = iDestructionPriority;
00184     if (SingletonGuard* guard = SingletonGuard::instance())
00185     {
00186         guard->subscribe(this);
00187     }
00188 }
00189 
00190 
00191 
00192 /** Return true if iA has to be killed before iB.
00193  */
00194 bool CompareDestructionPriority::operator()(SingletonBase* iA, SingletonBase* iB)
00195 {
00196     LASS_ASSERT(iA != 0 && iB != 0);
00197     return iA->destructionPriority() < iB->destructionPriority();
00198 }
00199 
00200 
00201 
00202 /** on destruction, you have to kill all singletons.
00203  *  
00204  *  This isn't thread safe as it is, but that's ok because it's only "caller" singletonCleanUp()
00205  *  is already locking on SingletonGuard level for us.
00206  */
00207 SingletonGuard::~SingletonGuard()
00208 {
00209 #if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_INTEL && !defined(_DEBUG)
00210 #   pragma LASS_FIXME("~SingletonGuard causes access violation => hacked: leak resources")
00211 #else
00212     while (!deathRow_.empty())
00213     {
00214         SingletonBase* deadManWalking = deathRow_.top();
00215         deathRow_.pop();
00216         delete deadManWalking;
00217     }
00218 #endif
00219     deadReference(true);
00220 }
00221 
00222 
00223 
00224 /** subscribe singleton to the destruction list
00225  *  
00226  *  This isn't thread safe as it is, but that's ok because it's only indirect caller 
00227  *  Singleton::instance() is already locking on SingletonGuard level for us.
00228  */
00229 void SingletonGuard::subscribe(SingletonBase* iSingleton)
00230 {
00231     deathRow_.push(iSingleton);
00232 }
00233 
00234 
00235 
00236 /** return the SingletonGuard singleton instance =)
00237  */
00238 SingletonGuard* SingletonGuard::instance()
00239 {
00240     if (deadReference(false))
00241     {
00242         std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR: Usage of dead reference to SingletonGuard!"
00243             << std::endl;
00244         return 0;
00245     }
00246 
00247     if (!instance_)
00248     {
00249         CustomSemaphore lock;
00250         if (!instance_) // double check
00251         {
00252             instance_ = new SingletonGuard;
00253             ::atexit(&SingletonGuard::killEmAll);
00254         }
00255     }
00256 
00257     return instance_;
00258 }
00259 
00260 
00261 
00262 /** kills singleton guard and all its guarded singleton.
00263  *  @relates SingletonGuard
00264  *
00265  *  This function is subscribed to ::atexit to kill all singletons at exit of the application<
00266  */
00267 void SingletonGuard::killEmAll()
00268 {
00269     CustomSemaphore lock;
00270     delete instance_;
00271     instance_ = 0;
00272 }
00273 
00274 
00275 
00276 /** return true if singleton guard is destructed.
00277  *  @param iSetReferenceToDead - call this method with true on destruction of singleton
00278  *                              - call this method with false to check it.
00279  */
00280 bool SingletonGuard::deadReference(bool iSetReferenceToDead)
00281 {
00282     static bool dead = false;
00283 
00284     if (iSetReferenceToDead)
00285     {
00286         dead = true;
00287     }
00288 
00289     return dead;
00290 }
00291 
00292 
00293 
00294 }
00295 
00296 }
00297 
00298 }

Generated on Mon Nov 10 14:21:34 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo