CrystalSpace

Public API Reference

csutil/spinlock.h

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2006 by Frank Richter
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 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     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public
00015     License along with this library; if not, write to the Free
00016     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 */
00018 
00019 #ifndef __CS_CSUTIL_SPINLOCK_H__
00020 #define __CS_CSUTIL_SPINLOCK_H__
00021 
00025 #ifndef CS_PLATFORM_WIN32
00026 #include <pthread.h>
00027 #include <sched.h>
00028 #endif
00029 
00033 namespace CS
00034 {
00035   class SpinLock
00036   {
00037   #ifdef CS_PLATFORM_WIN32
00038     typedef DWORD ThreadID;
00039   #else
00040     typedef pthread_t ThreadID;
00041   #endif
00042   #if defined (CS_PROCESSOR_X86) && defined (CS_COMPILER_GCC)
00043     volatile ThreadID threadid;
00044     volatile uint l;
00045   #elif defined(CS_PLATFORM_WIN32)
00046     volatile ThreadID threadid;
00047     volatile LONG l;
00048   #else
00049     pthread_mutex_t l;
00050   #endif
00051     
00052     volatile uint c;
00053     
00054     static const int spinsPerYield = 63;
00055     
00056   #if defined(CS_PLATFORM_WIN32)
00057     CS_FORCEINLINE ThreadID CurrentThreadID()
00058     { return GetCurrentThreadId(); }
00059   #else
00060     CS_FORCEINLINE ThreadID CurrentThreadID()
00061     { return pthread_self(); }
00062   #endif  
00063     
00064     // Spinlock implementation from ptmalloc3's malloc.c
00065   #if defined (CS_PROCESSOR_X86) && defined (CS_COMPILER_GCC)
00066     CS_FORCEINLINE bool DoLockWait()
00067     {
00068       ThreadID mythreadid = CurrentThreadID();
00069       if(mythreadid == threadid)
00070         ++c;
00071       else 
00072       {
00073         int spins = 0;
00074         for (;;) {
00075           int ret;
00076           __asm__ __volatile__ ("lock; cmpxchgl %2,(%1)" : "=a" (ret) : "r" (&l), "r" (1), "a" (0));
00077           if(!ret) 
00078           {
00079             CS_ASSERT(!threadid);
00080             threadid = mythreadid;
00081             c = 1;
00082             break;
00083           }
00084           if ((++spins & spinsPerYield) == 0) {
00085     #if defined (__SVR4) && defined (__sun) /* solaris */
00086             thr_yield();
00087     #elif defined(linux) || defined(CS_PLATFORM_MACOSX)
00088             sched_yield();
00089     #elif defined(CS_PLATFORM_WIN32)
00090             SleepEx (0, FALSE);
00091     #else  /* no-op yield on unknown systems */
00092             ;
00093     #endif /* solaris, linux, CS_PLATFORM_MACOSX, CS_PLATFORM_WIN32 */
00094           }
00095         }
00096       }
00097       return true;
00098     }
00099     CS_FORCEINLINE bool DoLockTry()
00100     {
00101       int ret;
00102       __asm__ __volatile__ ("lock; cmpxchgl %2,(%1)" : "=a" (ret) : "r" (&l), "r" (1), "a" (0));
00103       if(!ret){
00104         CS_ASSERT(!threadid);
00105         threadid = CurrentThreadID();
00106         c=1;
00107         return true;
00108       }
00109       return false;
00110     }
00111     CS_FORCEINLINE void DoRelease()
00112     {
00113       int ret;
00114       CS_ASSERT(CurrentThreadID() == threadid);
00115       if (!--c) {
00116         threadid=0;
00117         __asm__ __volatile__ ("xchgl %2,(%1)" : "=r" (ret) : "r" (&l), "0" (0));
00118       }
00119     }
00120     CS_FORCEINLINE void Init() { threadid = 0; c = 0; l = 0; }
00121     CS_FORCEINLINE void Destroy() {}
00122     //------------------------------------------------------------------------
00123   #elif defined(CS_PLATFORM_WIN32)
00124     CS_FORCEINLINE bool DoLockWait()
00125     {
00126       ThreadID mythreadid = CurrentThreadID();
00127       if(mythreadid == threadid)
00128         ++c;
00129       else {
00130         int spins = 0;
00131         for (;;) {
00132           if (!_InterlockedExchange(&l, 1)) {
00133             CS_ASSERT(!threadid);
00134             threadid = mythreadid;
00135             c = 1;
00136             break;
00137           }
00138           if ((++spins & spinsPerYield) == 0)
00139             SleepEx (0, FALSE);
00140         }
00141       }
00142       return true;
00143     }
00144     CS_FORCEINLINE bool DoLockTry()
00145     {
00146       if (!_InterlockedExchange (&l, 1)) {
00147         CS_ASSERT (!threadid);
00148         threadid = CurrentThreadID();
00149         c = 1;
00150         return true;
00151       }
00152       return false;
00153     }
00154     CS_FORCEINLINE void DoRelease()
00155     {
00156       CS_ASSERT (CurrentThreadID() == threadid);
00157       if (!--c) {
00158         threadid = 0;
00159         _InterlockedExchange (&l, 0);
00160       }
00161     }
00162     CS_FORCEINLINE void Init() { threadid = 0; c = 0; l = 0; }
00163     CS_FORCEINLINE void Destroy() {}
00164     //------------------------------------------------------------------------
00165   #else
00166     CS_FORCEINLINE bool DoLockWait()
00167     {
00168       if(!pthread_mutex_lock(&l)){
00169         c++;
00170         return true;
00171       }
00172       return false;
00173     }
00174     CS_FORCEINLINE bool DoLockTry()
00175     {
00176       if(!pthread_mutex_trylock(&l)){
00177         c++;
00178         return true;
00179       }
00180       return false;
00181     }
00182     CS_FORCEINLINE void DoRelease()
00183     {
00184        --c;
00185       pthread_mutex_unlock(&l);
00186     }
00187     CS_FORCEINLINE void Init() 
00188     {
00189       pthread_mutexattr_t attr;
00190       c=0;
00191       if(pthread_mutexattr_init (&attr)) return;
00192       if(pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE)) return;
00193       if(pthread_mutex_init (&l, &attr)) return;
00194       pthread_mutexattr_destroy (&attr);
00195     }
00196     CS_FORCEINLINE void Destroy() {}
00197 #endif
00198   public:
00199     SpinLock()
00200     { Init(); }
00201     ~SpinLock() { Destroy(); }
00202   
00203     CS_FORCEINLINE bool LockWait()
00204     { return DoLockWait(); }
00205     CS_FORCEINLINE bool LockTry()
00206     { return DoLockTry(); }
00207     CS_FORCEINLINE void Release()
00208     { DoRelease(); }
00209   };
00210 } // namespace CS
00211 
00214 #endif // __CS_CSUTIL_SPINLOCK_H__

Generated for Crystal Space by doxygen 1.4.7