library of assembled shared sources

http://lass.cocamware.com

thread.h

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 *   @defgroup   Threading Threading
00045 *   Threading functions.  The Mutex and Thread classes for WIN32 are almost literal copy and pastes
00046 *   of the functions of wxWindows2.4.2.  The implementation has changed a little bit to
00047 *   use more of the LASS utility functions.  The most noticible difference is in the thread
00048 *   class where the Delete is not implemented, due to too large dependencies.  The Critical
00049 *   Section on WIN32 is an own implementation, just like all the UNIX Posix threading stuff.
00050 *
00051 *   A little word on synchronization: in windows there are quite a few synchronisation
00052 *   primitives and synchronized objects.  Only the most common are implemented and wrapped
00053 *   in the LASS environment:
00054 *       Mutex : system-wide synchronisation object between processes/threads.  Mutexes set in one process can be
00055 *               unlocked in another one.  That is the theory.  In practice with the LASS
00056 *               wrapping, there is no possibility to reference these mutexes in other
00057 *               processes, named mutexes are not supported.  Mutex are rather heavyweight
00058 *               structures.  Cannot be used for intrathread synchronisation AFAIK [TDM]
00059 *       CriticalSection : process-wide synchronisation object between threads.  Lightweight
00060 *               construction.  Cannot be used for intra-thread synchronisation.
00061 *       Condition: this corresponds with MS Windows Events, can be used for interprocess/
00062 *               interthread and intrathread synchronisation. Not implemented in UNIX build.
00063 */
00064 
00065 #ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_THREAD_H
00066 #define LASS_GUARDIAN_OF_INCLUSION_UTIL_THREAD_H
00067 
00068 #include "util_common.h"
00069 #include "non_copyable.h"
00070 #include "atomic.h"
00071 
00072 namespace lass
00073 {
00074 namespace util
00075 {
00076 namespace impl
00077 {
00078     class MutexInternal;
00079     class ConditionInternal;
00080     class ThreadInternal;
00081     class ThreadLocalStorageInternal;
00082 }
00083 
00084 /** Return code for lock functions
00085  *  @ingroup Threading
00086  *  @see Mutex,CriticalSection
00087  */
00088 enum LockResult
00089 {
00090     lockSuccess,        /**< Mutex/CriticalSection is succesfully locked by this thread */
00091     lockBusy        /**< Mutex/CriticalSection is locked by another thread */
00092 };
00093 
00094 /** Return code for wait functions
00095  *  @ingroup Threading
00096  *  @see Condition
00097  */
00098 enum WaitResult
00099 {
00100     waitSuccess,    /**< Wait is successfully terminated */ 
00101     waitTimeout /**< Wait failed because of a timeout */
00102 };
00103 
00104 /** ThreadKind.
00105 *   @ingroup Threading
00106 *   @see Thread
00107 */
00108 enum ThreadKind
00109 {
00110     threadDetached,     /**< detached thread */
00111     threadJoinable      /**< joinable thread, can be waited for */
00112 };
00113 
00114 /** @ingroup Threading
00115  */
00116 LASS_DLL extern unsigned numberOfProcessors;
00117 
00118 /** Mutex.
00119 *   @ingroup Threading
00120 *   @see MutexLocker
00121 *   A mutex is a systemwide synchronization object. It is heavier in use than a critical
00122 *   section.  For exception-safe basic use, the MutexLocker can be used.
00123 */
00124 class LASS_DLL Mutex : NonCopyable
00125 {
00126 public:
00127     Mutex();
00128     ~Mutex();
00129     void lock();
00130     const LockResult tryLock();
00131     void unlock();
00132     const bool isLocked() const;
00133 
00134 private:
00135     impl::MutexInternal* pimpl_;
00136 };
00137 
00138 /** CriticalSection.
00139 *   @ingroup Threading
00140 *   @see CriticalSectionLocker
00141 *   @author Tom De Muer [TDM]
00142 *   @date 2004
00143 *   Process wide synchronization object.  The recommended object for interthread synchronisation
00144 *   within a process in Windows.
00145 */
00146 class LASS_DLL CriticalSection : NonCopyable
00147 {
00148 public:
00149     CriticalSection();
00150     ~CriticalSection();
00151     
00152     void lock();
00153     const LockResult tryLock();
00154     void unlock();
00155     
00156     const bool isLocked() const;
00157 
00158 private:
00159     void* pimpl_;
00160 };
00161 
00162 
00163 
00164 /** Condition.
00165 *   @ingroup Threading
00166 *   A condition can be used to synchronize using messages were a condition waits for
00167 *   a signal or broadcast.  A signal will only release one waiter, a broadcast will release
00168 *   all current waiting wait-conditions.
00169 */
00170 class LASS_DLL Condition: NonCopyable
00171 {
00172 public:
00173     Condition();
00174     ~Condition();
00175     void wait();
00176     const WaitResult wait(unsigned long iMilliSeconds);
00177     void signal();
00178     void broadcast();
00179 private:
00180     impl::ConditionInternal* pimpl_;
00181 };
00182 
00183 /** A base class for threads.
00184 *   @ingroup Threading
00185 *   The virtual function doRun() needs to be overriden and after creation of the 
00186 *   thread on the heap or stack, the thread is in a state ready to be run.  
00187 *   The run() function actually starts the thread.
00188 *
00189 *   JOINABLE threads can be waited for, DETACHED threads can not be waited for.
00190 */
00191 class LASS_DLL Thread: NonCopyable
00192 {
00193 public:
00194 
00195     enum 
00196     { 
00197         anyProcessor = unsigned(-1) /**< argument for Thread::bind to unbind the thread so it runs on any processor */
00198     };
00199 
00200     virtual ~Thread();
00201     
00202     void run();
00203     void join();
00204     void bind(unsigned processor);
00205     
00206     static void sleep(unsigned long milliseconds);
00207     static void yield();
00208     static void bindCurrent(unsigned processor);
00209 
00210 protected:
00211 
00212     Thread(ThreadKind kind = threadDetached, const char* name = 0);
00213 
00214 private:
00215 
00216     friend class impl::ThreadInternal;
00217 
00218     virtual void doRun() = 0;
00219 
00220     impl::ThreadInternal* pimpl_;
00221 };
00222 
00223 
00224 
00225 /** A primitive to provide Thread Local Storage functionality
00226  *  @ingroup Threading
00227  */
00228 class LASS_DLL ThreadLocalStorage: NonCopyable
00229 {
00230 public:
00231     ThreadLocalStorage(void (*destructor)(void*) = 0);
00232     ~ThreadLocalStorage();
00233     void* const get() const;
00234     void set(void* value);
00235 private:
00236     impl::ThreadLocalStorageInternal* pimpl_;
00237 };
00238 
00239 
00240 
00241 
00242 /** A primitive to provide Thread Local Storage functionality for a first-citizen class.
00243  *  @ingroup Threading
00244  *
00245  *  @arg requirements: T must be copyconstructible.
00246  *
00247  *  @warning 
00248  *      On Win32 platform, you should _access_ a ThreadLocalVariable inside code running
00249  *      from a util::Thread (or util::threadFun for that matter).  This is because the destructor
00250  *      of the variable will _only_ be called on exit of a util::Thread.  If you access it from
00251  *      any other thread (even the main thread), the variable will correctly constructed for that
00252  *      thread, but it will _never_ be destructed!
00253  *      On a POSIX platform, this is not a problem though!  The destructor will always be called.
00254  */
00255 template <typename T>
00256 class ThreadLocalVariable: NonCopyable
00257 {
00258 public:
00259     typedef typename CallTraits<T>::TValue TValue;
00260     typedef typename CallTraits<T>::TParam TParam;
00261     typedef typename CallTraits<T>::TConstReference TConstReference;
00262     typedef typename CallTraits<T>::TReference TReference;
00263 
00264     explicit ThreadLocalVariable(TParam prototype = T()):
00265         prototype_(prototype),
00266         storage_(&ThreadLocalVariable<T>::destructor)
00267     {
00268     }
00269 
00270     TValue* const get()
00271     {
00272         TValue* ptr = static_cast<TValue*>(storage_.get());
00273         if (!ptr)
00274         {
00275             std::auto_ptr<T> newCopy(new TValue(prototype_));
00276             storage_.set(newCopy.get());
00277             ptr = newCopy.release();
00278         }
00279         return ptr;
00280     }
00281 
00282     const TValue* const get() const
00283     {
00284         const TValue* ptr = static_cast<const TValue*>(storage_.get());
00285         if (!ptr)
00286         {
00287             std::auto_ptr<T> newCopy(new TValue(prototype_));
00288             storage_.set(newCopy.get());
00289             ptr = newCopy.release();
00290         }
00291         return ptr;
00292     }
00293 
00294     TValue* const operator->()
00295     {
00296         return get();
00297     }
00298 
00299     const TValue* const operator->() const
00300     {
00301         return get();
00302     }
00303 
00304     TReference operator*()
00305     {
00306         return *get();
00307     }
00308 
00309     TConstReference operator*() const
00310     {
00311         return *get();
00312     }
00313 
00314 private:
00315 
00316     static void destructor(void* p)
00317     {
00318         delete static_cast<T*>(p);
00319     }
00320 
00321     TValue prototype_;
00322     mutable ThreadLocalStorage storage_;
00323 };
00324 
00325 
00326 
00327 /** Lean and mean synchronisation object, without OS support.
00328 *   @ingroup Threading
00329 *   @see SemaphoreLocker
00330 *   @author Bram de Greve
00331 *   @date 2004
00332 *   
00333 *   This semaphore is built upon atomic operations that are programmed in assembly.
00334 */
00335 class LASS_DLL Semaphore : NonCopyable
00336 {
00337 public:
00338     Semaphore(int iNumberOfSlots = 1): 
00339         freeSlots_(std::min(1, iNumberOfSlots))
00340     {
00341     }
00342     
00343     void lock()
00344     {
00345         int oldSlots, newSlots;
00346         do
00347         {
00348             oldSlots = freeSlots_;
00349             LASS_ASSERT(oldSlots >= 0);
00350             newSlots = oldSlots - 1;
00351         }
00352         while (oldSlots == 0 || !atomicCompareAndSwap(freeSlots_, oldSlots, newSlots));
00353     }
00354     const LockResult tryLock()
00355     {
00356         int oldSlots, newSlots;
00357         do
00358         {
00359             oldSlots = freeSlots_;
00360             LASS_ASSERT(oldSlots >= 0);
00361             if (oldSlots == 0)
00362             {
00363                 return lockBusy;
00364             }
00365             newSlots = oldSlots - 1;
00366         }
00367         while (!atomicCompareAndSwap(freeSlots_, oldSlots, newSlots));
00368         return lockSuccess;
00369     }
00370     void unlock() 
00371     { 
00372         atomicIncrement(freeSlots_); 
00373     }
00374     const bool isLocked() const 
00375     { 
00376         return freeSlots_ == 0; 
00377     }
00378 
00379 private:
00380     volatile int freeSlots_;
00381 };
00382 
00383 
00384 
00385 
00386 
00387 /** Common base class for lockers
00388  *  @ingroup Threading
00389  */
00390 class LockerBase
00391 {
00392 public:
00393     operator bool() const { return false; }
00394 };
00395 
00396 /** @ingroup Threading
00397  */
00398 template <typename LockType>
00399 class Locker: public LockerBase
00400 {
00401 public:
00402     typedef LockType TLock;
00403     Locker(TLock& iLock): 
00404         lock_(&iLock) 
00405     { 
00406         lock_->lock(); 
00407     }
00408     ~Locker() 
00409     {
00410         try
00411         {
00412             lock_->unlock();
00413         }
00414         catch (std::exception& error)
00415         {
00416             std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: "
00417                 << "exception thrown in ~Locker(): " << error.what() << std::endl;
00418         }
00419         catch (...)
00420         {
00421             std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: "
00422                 << "unknown exception thrown in ~Locker()" << std::endl;
00423         }
00424     }
00425     TLock& mutex()
00426     { 
00427         LASS_ASSERT(lock_);
00428         return *lock_;
00429     }
00430     void swap(Locker& iOther)
00431     {
00432         std::swap(lock_, iOther.lock_);
00433     }
00434 private:
00435     TLock* lock_;
00436 };
00437 
00438 /** @ingroup Threading
00439  *  @relates Locker
00440  */
00441 template <typename T>
00442 inline Locker<T> makeLocker(T& iLock)
00443 {
00444     return Locker<T>(iLock);
00445 }
00446 
00447 /** typedef of Locker for Mutex
00448  *  @ingroup Threading
00449  *  @sa Mutex
00450  *  @sa Locker
00451 */
00452 typedef Locker<Mutex> MutexLocker;
00453 
00454 /** typedef of Locker for CriticalSection
00455  *  @ingroup Threading
00456  *  @sa CriticalSection
00457  *  @sa Locker
00458 */
00459 typedef Locker<CriticalSection> CriticalSectionLocker;
00460 
00461 /** typedef of Locker for Semaphore
00462  *  @ingroup Threading
00463  *  @sa Semaphore
00464  *  @sa Locker
00465 */
00466 typedef Locker<Semaphore> SemaphoreLocker;
00467 
00468 }
00469 
00470 }
00471 
00472 /** @brief Locks a @a iLock and starts a scope block in which it remains locked.
00473  *  @author [Bramz]
00474  *  @ingroup Threading
00475  *
00476  *  This macro starts a scope block through a @c if/else construct where the lock will be locked
00477  *  on entering the scope, and will be unlocked on leaving the scope.  You can use this scope in two
00478  *  ways: by using the braces:
00479  *
00480  *  @code
00481  *  LASS_LOCK(lock)
00482  *  {
00483  *      // locked
00484  *      doSomethingWhileLocked();
00485  *  }
00486  *  // unlocked
00487  *  @endcode
00488  *
00489  *  Because the macro uses an @c if/else construct, you can ommit the braces if you only need one
00490  *  statement in the locked scope:
00491  *
00492  *  @code
00493  *  LASS_LOCK(lock) doSomethingWhileLocked();
00494  *  @endcode
00495  *
00496  *  @par requirements for @a iLock:
00497  *      @a iLock can be of any type that implements two methods @c lock() and @c unlock(), that have
00498  *      to do the obvious thing.  Two excellent types to be used are lass::util::Mutex and
00499  *      lass::util::CriticalSection.
00500  *
00501  *  @par references:
00502  *      <i>Concurrent Acces Control & C++</i>, C/C++ Users Journal, January 2004
00503  *
00504  *  @note Guess what, for some reason bill gates might know,
00505  *      @c LASS_UNIQUENAME(lassUtilImplGenericLocker) causes troubles in the @c win32_vc7_d build.
00506  *      huray!  Does the concatenation fails?  It seems not, since in the preprocessor files
00507  *      everything seems fine.  And yet, it breaks.  Hence, we had to give up on it, there's only
00508  *      @c lassUtilImplGenericLocker now.  I don't think that will cause any problems, since it's
00509  *      inside an @c if/else scope block.  But if you get name conflicts on LASS_LOCK, this might
00510  *      be the reason ...
00511  */
00512 #define LASS_LOCK(iLock)\
00513     if (const ::lass::util::LockerBase& LASS_UNUSED(lassUtilImplLocker) =\
00514         ::lass::util::makeLocker(iLock))\
00515     {\
00516         LASS_ASSERT_UNREACHABLE;\
00517     }\
00518     else
00519 
00520 #endif

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