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.6.1