csutil/spinlock.h Source File (Crystal Space 2.0 Public API Reference)

| ## Public API Reference

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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__