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