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(CS_PLATFORM_UNIX)
00086             sched_yield();
00087     #elif defined(CS_PLATFORM_WIN32)
00088             SleepEx (0, FALSE);
00089     #else  /* no-op yield on unknown systems */
00090             ;
00091     #endif /* CS_PLATFORM_UNIX, CS_PLATFORM_WIN32 */
00092           }
00093         }
00094       }
00095       return true;
00096     }
00097     CS_FORCEINLINE bool DoLockTry()
00098     {
00099       int ret;
00100       __asm__ __volatile__ ("lock; cmpxchgl %2,(%1)" : "=a" (ret) : "r" (&l), "r" (1), "a" (0));
00101       if(!ret){
00102         CS_ASSERT(!threadid);
00103         threadid = CurrentThreadID();
00104         c=1;
00105         return true;
00106       }
00107       return false;
00108     }
00109     CS_FORCEINLINE void DoRelease()
00110     {
00111       int ret;
00112       CS_ASSERT(CurrentThreadID() == threadid);
00113       if (!--c) {
00114         threadid=0;
00115         __asm__ __volatile__ ("xchgl %2,(%1)" : "=r" (ret) : "r" (&l), "0" (0));
00116       }
00117     }
00118     CS_FORCEINLINE void Init() { threadid = 0; c = 0; l = 0; }
00119     CS_FORCEINLINE void Destroy() {}
00120     //------------------------------------------------------------------------
00121   #elif defined(CS_PLATFORM_WIN32)
00122     CS_FORCEINLINE bool DoLockWait()
00123     {
00124       ThreadID mythreadid = CurrentThreadID();
00125       if(mythreadid == threadid)
00126         ++c;
00127       else {
00128         int spins = 0;
00129         for (;;) {
00130           if (!_InterlockedExchange(&l, 1)) {
00131             CS_ASSERT(!threadid);
00132             threadid = mythreadid;
00133             c = 1;
00134             break;
00135           }
00136           if ((++spins & spinsPerYield) == 0)
00137             SleepEx (0, FALSE);
00138         }
00139       }
00140       return true;
00141     }
00142     CS_FORCEINLINE bool DoLockTry()
00143     {
00144       if (!_InterlockedExchange (&l, 1)) {
00145         CS_ASSERT (!threadid);
00146         threadid = CurrentThreadID();
00147         c = 1;
00148         return true;
00149       }
00150       return false;
00151     }
00152     CS_FORCEINLINE void DoRelease()
00153     {
00154       CS_ASSERT (CurrentThreadID() == threadid);
00155       if (!--c) {
00156         threadid = 0;
00157         _InterlockedExchange (&l, 0);
00158       }
00159     }
00160     CS_FORCEINLINE void Init() { threadid = 0; c = 0; l = 0; }
00161     CS_FORCEINLINE void Destroy() {}
00162     //------------------------------------------------------------------------
00163   #else
00164     CS_FORCEINLINE bool DoLockWait()
00165     {
00166       if(!pthread_mutex_lock(&l)){
00167         c++;
00168         return true;
00169       }
00170       return false;
00171     }
00172     CS_FORCEINLINE bool DoLockTry()
00173     {
00174       if(!pthread_mutex_trylock(&l)){
00175         c++;
00176         return true;
00177       }
00178       return false;
00179     }
00180     CS_FORCEINLINE void DoRelease()
00181     {
00182        --c;
00183       pthread_mutex_unlock(&l);
00184     }
00185     CS_FORCEINLINE void Init() 
00186     {
00187       pthread_mutexattr_t attr;
00188       c=0;
00189       if(pthread_mutexattr_init (&attr)) return;
00190       if(pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE)) return;
00191       if(pthread_mutex_init (&l, &attr)) return;
00192       pthread_mutexattr_destroy (&attr);
00193     }
00194     CS_FORCEINLINE void Destroy() {}
00195 #endif
00196   public:
00197     SpinLock()
00198     { Init(); }
00199     ~SpinLock() { Destroy(); }
00200   
00201     CS_FORCEINLINE bool LockWait()
00202     { return DoLockWait(); }
00203     CS_FORCEINLINE bool LockTry()
00204     { return DoLockTry(); }
00205     CS_FORCEINLINE void Release()
00206     { DoRelease(); }
00207   };
00208 } // namespace CS
00209 
00212 #endif // __CS_CSUTIL_SPINLOCK_H__

Generated for Crystal Space 2.0 by doxygen 1.7.6.1