Rapicorn - Experimental UI Toolkit - Source Code 10.08.1
rapicornthread.hh
Go to the documentation of this file.
00001 /* Rapicorn
00002  * Copyright (C) 2006 Tim Janik
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * A copy of the GNU Lesser General Public License should ship along
00015  * with this library; if not, see http://www.gnu.org/copyleft/.
00016  */
00017 #ifndef __RAPICORN_THREAD_XX_HH__
00018 #define __RAPICORN_THREAD_XX_HH__
00019 
00020 #include <rcore/utilities.hh>
00021 
00022 namespace Rapicorn {
00023 
00024 class Thread;
00025 
00026 template<class Value> inline bool once_enter (volatile Value *value_location);
00027 template<class Value> inline void once_leave (volatile Value *value_location,
00028                                               Value           initialization_value);
00029 
00030 class Mutex : protected NonCopyable {
00031   RapicornMutex mutex;
00032   friend class Cond;
00033 public:
00034   explicit      Mutex   ();
00035   void          lock    ();
00036   void          unlock  ();
00037   bool          trylock (); // TRUE indicates success
00038   /*Des*/       ~Mutex  ();
00039 };
00040 
00041 class RecMutex : protected NonCopyable {
00042   RapicornRecMutex rmutex;
00043 public:
00044   explicit      RecMutex  ();
00045   void          lock      ();
00046   void          unlock    ();
00047   bool          trylock   ();
00048   /*Des*/       ~RecMutex ();
00049 };
00050 
00051 class Cond : protected NonCopyable {
00052   RapicornCond cond;
00053 public:
00054   explicit      Cond          ();
00055   void          signal        ();
00056   void          broadcast     ();
00057   void          wait          (Mutex &m);
00058   void          wait_timed    (Mutex &m, int64 max_usecs);
00059   /*Des*/       ~Cond         ();
00060 };
00061 
00062 class SpinLock {
00063   volatile union {
00064     Mutex *fallback;                                    // union needs pointer alignment
00065     char   chars[RAPICORN_SIZEOF_PTHREADH_SPINLOCK];    // char may_alias any type
00066   } spinspace;
00067 public:
00068   explicit SpinLock ();
00069   void     lock     ();
00070   void     unlock   ();
00071   bool     trylock  ();
00072   /*Des*/ ~SpinLock ();
00073 };
00074 
00075 namespace Atomic {
00076 inline void    read_barrier  (void)                           { __sync_synchronize(); }
00077 inline void    write_barrier (void)                           { __sync_synchronize(); }
00078 inline void    full_barrier  (void)                           { __sync_synchronize(); }
00079 /* atomic values */
00080 template<class V> inline void value_set (volatile V *value_addr, V n)      { while (!__sync_bool_compare_and_swap (value_addr, *value_addr, n)); }
00081 template<class V> inline V    value_get (volatile V *value_addr)           { return __sync_fetch_and_add (value_addr, 0); }
00082 template<class V> inline bool value_cas (volatile V *value_addr, V o, V n) { return __sync_bool_compare_and_swap (value_addr, o, n); }
00083 template<class V> inline V    value_add (volatile V *value_addr, V diff)   { return __sync_fetch_and_add (value_addr, diff); }
00084 /* atomic numeric operations */
00085 #define RAPICORN_ATOMIC_OPS(type) \
00086   inline void set (volatile type  *p, type v)         { return value_set (p, v); } \
00087   inline int  get (volatile type  *p)                 { return value_get (p); } \
00088   inline bool cas (volatile type  *p, type o, type n) { return value_cas (p, o, n); } \
00089   inline int  add (volatile type  *p, type d)         { return value_add (p, d); }
00090 RAPICORN_ATOMIC_OPS (int);
00091 RAPICORN_ATOMIC_OPS (uint);
00092 RAPICORN_ATOMIC_OPS (int64);
00093 RAPICORN_ATOMIC_OPS (uint64);
00094 /* atomic pointer operations */
00095 template<class V> inline void ptr_set (V* volatile *ptr_addr, V *n)      { while (!__sync_bool_compare_and_swap (ptr_addr, *ptr_addr, n)); }
00096 template<class V> inline V*   ptr_get (V* volatile *ptr_addr)            { return __sync_fetch_and_add (ptr_addr, 0); }
00097 template<class V> inline V*   ptr_get (V* volatile const *ptr_addr)      { return __sync_fetch_and_add (ptr_addr, 0); }
00098 template<class V> inline bool ptr_cas (V* volatile *ptr_adr, V *o, V *n) { return __sync_bool_compare_and_swap (ptr_adr, o, n); }
00099 } // Atomic
00100 
00101 class OwnedMutex : protected NonCopyable {
00102   RecMutex m_rec_mutex;
00103   Thread * volatile m_owner;
00104   uint     volatile m_count;
00105 public:
00106   explicit       OwnedMutex ();
00107   inline void    lock       ();
00108   inline bool    trylock    ();
00109   inline void    unlock     ();
00110   inline Thread* owner      ();
00111   inline bool    mine       ();
00112   /*Des*/       ~OwnedMutex ();
00113 };
00114 
00115 class Thread : public virtual BaseObject {
00116 protected:
00117   explicit              Thread          (const String      &name);
00118   virtual void          run             () = 0;
00119   virtual               ~Thread         ();
00120 public:
00121   void                  start           ();
00122   int                   pid             () const;
00123   String                name            () const;
00124   void                  queue_abort     ();
00125   void                  abort           ();
00126   bool                  aborted         ();
00127   void                  wakeup          ();
00128   bool                  running         ();
00129   void                  wait_for_exit   ();
00130   int                   last_affinity   () const;
00131   int                   affinity        (int cpu = -1);
00132   /* event loop */
00133   void                  exec_loop       ();
00134   void                  quit_loop       ();
00135   /* global methods */
00136   static void           emit_wakeups    (uint64             stamp);
00137   static Thread&        self            ();
00138   static int            online_cpus     ();
00139   /* Self thread */
00140   struct Self {
00141     static String       name            ();
00142     static void         name            (const String      &name);
00143     static bool         sleep           (long               max_useconds);
00144     static bool         aborted         ();
00145     static int          affinity        (int cpu = -1);
00146     static int          pid             ();
00147     static void         awake_after     (uint64             stamp);
00148     static void         set_wakeup      (RapicornThreadWakeup wakeup_func,
00149                                          void              *wakeup_data,
00150                                          void             (*destroy_data) (void*));
00151     static OwnedMutex&  owned_mutex     ();
00152     static void         yield           ();
00153     static void         exit            (void              *retval = NULL) RAPICORN_NORETURN;
00154   };
00155   /* DataListContainer API */
00156   template<typename Type> inline void set_data    (DataKey<Type> *key,
00157                                                    Type           data) { thread_lock(); data_list.set (key, data); thread_unlock(); }
00158   template<typename Type> inline Type get_data    (DataKey<Type> *key)  { thread_lock(); Type d = data_list.get (key); thread_unlock(); return d; }
00159   template<typename Type> inline Type swap_data   (DataKey<Type> *key)  { thread_lock(); Type d = data_list.swap (key); thread_unlock(); return d; }
00160   template<typename Type> inline Type swap_data   (DataKey<Type> *key,
00161                                                    Type           data) { thread_lock(); Type d = data_list.swap (key, data); thread_unlock(); return d; }
00162   template<typename Type> inline void delete_data (DataKey<Type> *key)  { thread_lock(); data_list.del (key); thread_unlock(); }
00163   /* implementaiton details */
00164 private:
00165   DataList              data_list;
00166   RapicornThread       *bthread;
00167   OwnedMutex            m_omutex;
00168   int                   last_cpu;
00169   explicit              Thread          (RapicornThread      *thread);
00170   void                  thread_lock     ()                              { m_omutex.lock(); }
00171   bool                  thread_trylock  ()                              { return m_omutex.trylock(); }
00172   void                  thread_unlock   ()                              { m_omutex.unlock(); }
00173 protected:
00174   class ThreadWrapperInternal;
00175   static void threadxx_wrap   (RapicornThread *cthread);
00176   static void threadxx_delete (void         *cxxthread);
00177 };
00178 
00179 enum LockState { BALANCED = 0, AUTOLOCK = 1 };
00180 
00189 template<class MUTEX>
00190 class ScopedLock : protected NonCopyable {
00191   MUTEX         &m_mutex;
00192   volatile int   m_count;
00193 public:
00194   inline     ~ScopedLock () { while (m_count < 0) lock(); while (m_count > 0) unlock(); }
00195   inline void lock       () { m_mutex.lock(); m_count++; }
00196   inline void unlock     () { m_count--; m_mutex.unlock(); }
00197   inline      ScopedLock (MUTEX &mutex, LockState lockstate = AUTOLOCK) :
00198     m_mutex (mutex), m_count (0)
00199   { if (lockstate == AUTOLOCK) lock(); }
00200 };
00201 
00202 namespace Atomic {
00203 
00204 template<typename T>
00205 class RingBuffer : protected NonCopyable {
00206   const uint    m_size;
00207   T            *m_buffer;
00208   volatile uint m_wmark, m_rmark;
00209 public:
00210   explicit
00211   RingBuffer (uint bsize) :
00212     m_size (bsize + 1), m_wmark (0), m_rmark (bsize)
00213   {
00214     m_buffer = new T[m_size];
00215     Atomic::set (&m_wmark, 0);
00216     Atomic::set (&m_rmark, 0);
00217   }
00218   ~RingBuffer()
00219   {
00220     // Atomic::set (&m_size, 0);
00221     Atomic::set (&m_rmark, 0);
00222     Atomic::set (&m_wmark, 0);
00223     delete[] m_buffer;
00224   }
00225   uint
00226   n_writable()
00227   {
00228     const uint rm = Atomic::get (&m_rmark);
00229     const uint wm = Atomic::get (&m_wmark);
00230     uint space = (m_size - 1 + rm - wm) % m_size;
00231     return space;
00232   }
00233   uint
00234   write (uint     length,
00235          const T *data,
00236          bool     partial = true)
00237   {
00238     const uint orig_length = length;
00239     const uint rm = Atomic::get (&m_rmark);
00240     uint wm = Atomic::get (&m_wmark);
00241     uint space = (m_size - 1 + rm - wm) % m_size;
00242     if (!partial && length > space)
00243       return 0;
00244     while (length)
00245       {
00246         if (rm <= wm)
00247           space = m_size - wm + (rm == 0 ? -1 : 0);
00248         else
00249           space = rm - wm -1;
00250         if (!space)
00251           break;
00252         space = MIN (space, length);
00253         std::copy (data, &data[space], &m_buffer[wm]);
00254         wm = (wm + space) % m_size;
00255         data += space;
00256         length -= space;
00257       }
00258     Atomic::write_barrier();
00259     /* the write barrier ensures m_buffer writes are made visible before the m_wmark update */
00260     Atomic::set (&m_wmark, wm);
00261     return orig_length - length;
00262   }
00263   uint
00264   n_readable()
00265   {
00266     const uint wm = Atomic::get (&m_wmark);
00267     const uint rm = Atomic::get (&m_rmark);
00268     uint space = (m_size + wm - rm) % m_size;
00269     return space;
00270   }
00271   uint
00272   read (uint length,
00273         T   *data,
00274         bool partial = true)
00275   {
00276     const uint orig_length = length;
00277     Atomic::read_barrier();
00278     /* the read barrier ensures m_buffer writes are seen before m_wmark updates */
00279     const uint wm = Atomic::get (&m_wmark);
00280     uint rm = Atomic::get (&m_rmark);
00281     uint space = (m_size + wm - rm) % m_size;
00282     if (!partial && length > space)
00283       return 0;
00284     while (length)
00285       {
00286         if (wm < rm)
00287           space = m_size - rm;
00288         else
00289           space = wm - rm;
00290         if (!space)
00291           break;
00292         space = MIN (space, length);
00293         std::copy (&m_buffer[rm], &m_buffer[rm + space], data);
00294         rm = (rm + space) % m_size;
00295         data += space;
00296         length -= space;
00297       }
00298     Atomic::set (&m_rmark, rm);
00299     return orig_length - length;
00300   }
00301 };
00302 
00303 } // Atomic
00304 
00305 /* --- implementation --- */
00306 inline void
00307 OwnedMutex::lock ()
00308 {
00309   m_rec_mutex.lock();
00310   Atomic::ptr_set (&m_owner, &Thread::self());
00311   m_count++;
00312 }
00313 
00314 inline bool
00315 OwnedMutex::trylock ()
00316 {
00317   if (m_rec_mutex.trylock())
00318     {
00319       Atomic::ptr_set (&m_owner, &Thread::self());
00320       m_count++;
00321       return true; /* TRUE indicates success */
00322     }
00323   else
00324     return false;
00325 }
00326 
00327 inline void
00328 OwnedMutex::unlock ()
00329 {
00330   if (--m_count == 0)
00331     Atomic::ptr_set (&m_owner, (Thread*) 0);
00332   m_rec_mutex.unlock();
00333 }
00334 
00335 inline Thread*
00336 OwnedMutex::owner ()
00337 {
00338   return Atomic::ptr_get (&m_owner);
00339 }
00340 
00341 inline bool
00342 OwnedMutex::mine ()
00343 {
00344   return Atomic::ptr_get (&m_owner) == &Thread::self();
00345 }
00346 
00347 void once_list_enter  ();
00348 bool once_list_bounce (volatile void *ptr);
00349 bool once_list_leave  (volatile void *ptr);
00350 
00351 template<class Value> inline bool
00352 once_enter (volatile Value *value_location)
00353 {
00354   if (RAPICORN_LIKELY (Atomic::value_get (value_location) != 0))
00355     return false;
00356   else
00357     {
00358       once_list_enter();
00359       const bool initialized = Atomic::value_get (value_location) != 0;
00360       const bool needs_init = once_list_bounce (initialized ? NULL : value_location);
00361       return needs_init;
00362     }
00363 }
00364 
00365 template<class Value> inline void
00366 once_leave (volatile Value *value_location,
00367             Value           initialization_value)
00368 {
00369   RAPICORN_RETURN_IF_FAIL (Atomic::value_get (value_location) == 0);
00370   RAPICORN_RETURN_IF_FAIL (initialization_value != 0);
00371 
00372   Atomic::value_set (value_location, initialization_value);
00373   const bool found_and_removed = once_list_leave (value_location);
00374   RAPICORN_RETURN_IF_FAIL (found_and_removed == true);
00375 }
00376 
00377 } // Rapicorn
00378 
00379 #endif /* __RAPICORN_THREAD_XX_HH__ */
00380 
00381 /* vim:set ts=8 sts=2 sw=2: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines