atomic.H

00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2004 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
00018  */
00019 
00020 #ifndef ARCHON_UTILITIES_ATOMIC_H
00021 #define ARCHON_UTILITIES_ATOMIC_H
00022 
00023 #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
00024 #else // The default fall-back implementation (bad performance)
00025 #include <archon/util/mutex.H>
00026 #endif
00027 
00028 namespace Archon
00029 {
00030   namespace Utilities
00031   {
00032     using namespace std;
00033 
00054     struct Atomic
00055     {
00056       Atomic() {}
00057       explicit Atomic(int w) { set(w); }
00058 
00062       operator int() const { return get(); }
00063 
00067       Atomic &operator=(int w) { set(w);  return *this; }
00068 
00072       Atomic &operator++() { inc();  return *this; }
00073 
00077       int operator++(int) { return fetchAndAdd(1); }
00078 
00082       Atomic &operator--() { dec(); return *this; }
00083 
00087       int operator--(int) { return fetchAndAdd(-1); }
00088 
00092       Atomic &operator+=(int w) { add(w);  return *this; }
00093 
00097       Atomic &operator-=(int w) { add(-w); return *this; }
00098 
00104       int get() const;
00105 
00111       void set(int w);
00112 
00116       void inc();
00117 
00121       void dec();
00122 
00128       void add(int w);
00129 
00135       void sub(int w);
00136 
00144       bool decAndZeroTest();
00145 
00154       int fetchAndAdd(int w);
00155 
00166       bool testAndSet(int t, int w);
00167 
00168     private:
00169 
00170 #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
00171       volatile int v;
00172 #else // The default fall-back implementation (bad performance)
00173       Mutex m;
00174       int v;
00175 #endif
00176 
00177       Atomic(const Atomic &); // Hide
00178     };
00179 
00180 #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
00181 
00182     /*
00183      * On the platforms i486, i586, i686 and x86_64 we do not need
00184      * memory barriers to guarantee that a memory write on CPU 1 is
00185      * reflected imediately in a momory read by CPU 2. This is why
00186      * "memory" is never added to the clobber lists in the asm
00187      * statements below.
00188      */
00189 
00190     /*
00191      * We can just use a simple read. We always read from memory
00192      * since we have declared v to be volatile. Also, on the Intel
00193      * platform we do not need to concern ourselves with memory
00194      * ordering, thus we do not need memory barriers here (explicit
00195      * synchronizarion of memory update/access accross several CPUs.)
00196      */
00197     inline int Atomic::get() const
00198     {
00199       return v;
00200     }
00201 
00202     /*
00203      * We can just use a simple assignment. We always write to memory
00204      * since we have declared v to be volatile. Also, on the Intel
00205      * platform we do not need to concern ourselves with memory
00206      * ordering, thus we do not need memory barriers here (explicit
00207      * synchronizarion of memory update/access accross several CPUs.)
00208      */
00209     inline void Atomic::set(int w)
00210     {
00211       v = w;
00212     }
00213 
00214     inline void Atomic::inc()
00215     {
00216       asm volatile("lock; incl %0" : "=m"(v) : "m"(v));
00217     }
00218 
00219     inline void Atomic::dec()
00220     {
00221       asm volatile("lock; decl %0" : "=m"(v) : "m"(v));
00222     }
00223 
00224     inline void Atomic::add(int w)
00225     {
00226       asm volatile("lock; addl %1, %0" : "=m"(v) : "ir"(w), "m"(v));
00227     }
00228 
00229     inline void Atomic::sub(int w)
00230     {
00231       asm volatile("lock; subl %1, %0" : "=m"(v) : "ir"(w), "m"(v));
00232     }
00233 
00234     inline bool Atomic::decAndZeroTest()
00235     {
00236       unsigned char r;
00237       asm volatile("lock; decl %0; sete %1" : "=m"(v), "=qm"(r) : "m"(v));
00238       return r != 0;
00239     }
00240 
00241     inline int Atomic::fetchAndAdd(int w)
00242     {
00243       int r;
00244       asm volatile("lock; xaddl %0, %1" : "=r"(r), "=m"(v) : "0"(w), "m"(v));
00245       return r;
00246     }
00247 
00248     inline bool Atomic::testAndSet(int t, int w)
00249     {
00250       int r;
00251       asm volatile("lock; cmpxchgl %2, %1" : "=a"(r), "=m"(v) : "r"(w), "m"(v), "0"(t));
00252       return r == t;
00253     }
00254 
00255 #else // The default fall-back implementation (bad performance)
00256 
00257     inline int Atomic::get() const
00258     {
00259       Mutex::Lock l(m);
00260       return v;
00261     }
00262 
00263     inline void Atomic::set(int w)
00264     {
00265       Mutex::Lock l(m);
00266       v = w;
00267     }
00268 
00269     inline void Atomic::inc()
00270     {
00271       Mutex::Lock l(m);
00272       ++v;
00273     }
00274 
00275     inline void Atomic::dec()
00276     {
00277       Mutex::Lock l(m);
00278       --v;
00279     }
00280 
00281     inline void Atomic::add(int w)
00282     {
00283       Mutex::Lock l(m);
00284       v += w;
00285     }
00286 
00287     inline void Atomic::sub(int w)
00288     {
00289       Mutex::Lock l(m);
00290       v -= w;
00291     }
00292 
00293     inline bool Atomic::decAndZeroTest()
00294     {
00295       Mutex::Lock l(m);
00296       return --u == 0:
00297     }
00298 
00299     inline int Atomic::fetchAndAdd(int w)
00300     {
00301       Mutex::Lock l(m);
00302       int u = v;
00303       v += w;
00304       return u;
00305     }
00306 
00307     inline bool Atomic::testAndSet(int t, int w)
00308     {
00309       Mutex::Lock l(m);
00310       if(v!=t) return false;
00311       v = w;
00312       return true;
00313     }
00314 
00315 #endif
00316 
00317   }
00318 }
00319 
00320 #endif // ARCHON_UTILITIES_ATOMIC_H

Generated on Sun Jul 30 22:55:44 2006 for Archon by  doxygen 1.4.4