ovr_sdk

annotate LibOVR/Src/Kernel/OVR_Atomic.h @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
rev   line source
nuclear@0 1 /************************************************************************************
nuclear@0 2
nuclear@0 3 PublicHeader: OVR_Kernel.h
nuclear@0 4 Filename : OVR_Atomic.h
nuclear@0 5 Content : Contains atomic operations and inline fastest locking
nuclear@0 6 functionality. Will contain #ifdefs for OS efficiency.
nuclear@0 7 Have non-thread-safe implementaion if not available.
nuclear@0 8 Created : September 19, 2012
nuclear@0 9 Notes :
nuclear@0 10
nuclear@0 11 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
nuclear@0 12
nuclear@0 13 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
nuclear@0 14 you may not use the Oculus VR Rift SDK except in compliance with the License,
nuclear@0 15 which is provided at the time of installation or download, or which
nuclear@0 16 otherwise accompanies this software in either electronic or hard copy form.
nuclear@0 17
nuclear@0 18 You may obtain a copy of the License at
nuclear@0 19
nuclear@0 20 http://www.oculusvr.com/licenses/LICENSE-3.2
nuclear@0 21
nuclear@0 22 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
nuclear@0 23 distributed under the License is distributed on an "AS IS" BASIS,
nuclear@0 24 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
nuclear@0 25 See the License for the specific language governing permissions and
nuclear@0 26 limitations under the License.
nuclear@0 27
nuclear@0 28 ************************************************************************************/
nuclear@0 29
nuclear@0 30 #ifndef OVR_Atomic_h
nuclear@0 31 #define OVR_Atomic_h
nuclear@0 32
nuclear@0 33 #include "OVR_Types.h"
nuclear@0 34
nuclear@0 35 // Include System thread functionality.
nuclear@0 36 #if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE)
nuclear@0 37 #ifndef WIN32_LEAN_AND_MEAN
nuclear@0 38 #define WIN32_LEAN_AND_MEAN
nuclear@0 39 #endif
nuclear@0 40 #include <Windows.h>
nuclear@0 41 #else
nuclear@0 42 #include <pthread.h>
nuclear@0 43 #endif
nuclear@0 44
nuclear@0 45 #ifdef OVR_CC_MSVC
nuclear@0 46 #include <intrin.h>
nuclear@0 47 #pragma intrinsic(_ReadBarrier, _WriteBarrier, _ReadWriteBarrier)
nuclear@0 48 #endif
nuclear@0 49
nuclear@0 50 namespace OVR {
nuclear@0 51
nuclear@0 52
nuclear@0 53 // ****** Declared classes
nuclear@0 54
nuclear@0 55 // If there is NO thread support we implement AtomicOps and
nuclear@0 56 // Lock objects as no-ops. The other classes are not defined.
nuclear@0 57 template<class C> class AtomicOps;
nuclear@0 58 template<class T> class AtomicInt;
nuclear@0 59 template<class T> class AtomicPtr;
nuclear@0 60
nuclear@0 61 class Lock;
nuclear@0 62
nuclear@0 63
nuclear@0 64 //-----------------------------------------------------------------------------------
nuclear@0 65 // ***** AtomicOps
nuclear@0 66
nuclear@0 67 // Atomic operations are provided by the AtomicOps templates class,
nuclear@0 68 // implemented through system-specific AtomicOpsRaw specializations.
nuclear@0 69 // It provides several fundamental operations such as Exchange, ExchangeAdd
nuclear@0 70 // CompareAndSet, and Store_Release. Each function includes several memory
nuclear@0 71 // synchronization versions, important for multiprocessing CPUs with weak
nuclear@0 72 // memory consistency. The following memory fencing strategies are supported:
nuclear@0 73 //
nuclear@0 74 // - NoSync. No memory synchronization is done for atomic op.
nuclear@0 75 // - Release. All other memory writes are completed before atomic op
nuclear@0 76 // writes its results.
nuclear@0 77 // - Acquire. Further memory reads are forced to wait until atomic op
nuclear@0 78 // executes, guaranteeing that the right values will be seen.
nuclear@0 79 // - Sync. A combination of Release and Acquire.
nuclear@0 80
nuclear@0 81
nuclear@0 82 // *** AtomicOpsRaw
nuclear@0 83
nuclear@0 84 // AtomicOpsRaw is a specialized template that provides atomic operations
nuclear@0 85 // used by AtomicOps. This class has two fundamental qualities: (1) it
nuclear@0 86 // defines a type T of correct size, and (2) provides operations that work
nuclear@0 87 // atomically, such as Exchange_Sync and CompareAndSet_Release.
nuclear@0 88
nuclear@0 89 // AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw.
nuclear@0 90 // The primary thing is does is define sync class objects, whose destructor and
nuclear@0 91 // constructor provide places to insert appropriate synchronization calls, on
nuclear@0 92 // systems where such calls are necessary. So far, the breakdown is as follows:
nuclear@0 93 //
nuclear@0 94 // - X86 systems don't need custom syncs, since their exchange/atomic
nuclear@0 95 // instructions are implicitly synchronized.
nuclear@0 96 // - PowerPC requires lwsync/isync instructions that can use this mechanism.
nuclear@0 97 // - If some other systems require a mechanism where syncing type is associated
nuclear@0 98 // with a particular instruction, the default implementation (which implements
nuclear@0 99 // all Sync, Acquire, and Release modes in terms of NoSync and fence) may not
nuclear@0 100 // work. Ii that case it will need to be #ifdef-ed conditionally.
nuclear@0 101
nuclear@0 102 struct AtomicOpsRawBase
nuclear@0 103 {
nuclear@0 104 #if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
nuclear@0 105 // Need to have empty constructor to avoid class 'unused' variable warning.
nuclear@0 106 struct FullSync { inline FullSync() { } };
nuclear@0 107 struct AcquireSync { inline AcquireSync() { } };
nuclear@0 108 struct ReleaseSync { inline ReleaseSync() { } };
nuclear@0 109
nuclear@0 110 #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
nuclear@0 111 struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } };
nuclear@0 112 struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } };
nuclear@0 113 struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
nuclear@0 114
nuclear@0 115 #elif defined(OVR_CPU_MIPS)
nuclear@0 116 struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } };
nuclear@0 117 struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } };
nuclear@0 118 struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
nuclear@0 119
nuclear@0 120 #elif defined(OVR_CPU_ARM) // Includes Android and iOS.
nuclear@0 121 struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } };
nuclear@0 122 struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } };
nuclear@0 123 struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } };
nuclear@0 124
nuclear@0 125 #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4)
nuclear@0 126 // __sync functions are already full sync
nuclear@0 127 struct FullSync { inline FullSync() { } };
nuclear@0 128 struct AcquireSync { inline AcquireSync() { } };
nuclear@0 129 struct ReleaseSync { inline ReleaseSync() { } };
nuclear@0 130 #endif
nuclear@0 131 };
nuclear@0 132
nuclear@0 133
nuclear@0 134 // 4-Byte raw data atomic op implementation class.
nuclear@0 135 struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase
nuclear@0 136 {
nuclear@0 137 #if !defined(OVR_ENABLE_THREADS)
nuclear@0 138
nuclear@0 139 // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
nuclear@0 140 typedef uint32_t T;
nuclear@0 141
nuclear@0 142 // *** Thread - Safe Atomic Versions.
nuclear@0 143
nuclear@0 144 #elif defined(OVR_OS_MS)
nuclear@0 145
nuclear@0 146 // Use special defined for VC6, where volatile is not used and
nuclear@0 147 // InterlockedCompareExchange is declared incorrectly.
nuclear@0 148 typedef LONG T;
nuclear@0 149 #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300)
nuclear@0 150 typedef T* InterlockTPtr;
nuclear@0 151 typedef LPVOID ET;
nuclear@0 152 typedef ET* InterlockETPtr;
nuclear@0 153 #else
nuclear@0 154 typedef volatile T* InterlockTPtr;
nuclear@0 155 typedef T ET;
nuclear@0 156 typedef InterlockTPtr InterlockETPtr;
nuclear@0 157 #endif
nuclear@0 158 inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); }
nuclear@0 159 inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); }
nuclear@0 160 inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; }
nuclear@0 161
nuclear@0 162 #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
nuclear@0 163 typedef uint32_t T;
nuclear@0 164 static inline uint32_t Exchange_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 165 {
nuclear@0 166 uint32_t ret;
nuclear@0 167
nuclear@0 168 asm volatile("1:\n\t"
nuclear@0 169 "lwarx %[r],0,%[i]\n\t"
nuclear@0 170 "stwcx. %[j],0,%[i]\n\t"
nuclear@0 171 "bne- 1b\n"
nuclear@0 172 : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
nuclear@0 173
nuclear@0 174 return ret;
nuclear@0 175 }
nuclear@0 176
nuclear@0 177 static inline uint32_t ExchangeAdd_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 178 {
nuclear@0 179 uint32_t dummy, ret;
nuclear@0 180
nuclear@0 181 asm volatile("1:\n\t"
nuclear@0 182 "lwarx %[r],0,%[i]\n\t"
nuclear@0 183 "add %[o],%[r],%[j]\n\t"
nuclear@0 184 "stwcx. %[o],0,%[i]\n\t"
nuclear@0 185 "bne- 1b\n"
nuclear@0 186 : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
nuclear@0 187
nuclear@0 188 return ret;
nuclear@0 189 }
nuclear@0 190
nuclear@0 191 static inline bool CompareAndSet_NoSync(volatile uint32_t *i, uint32_t c, uint32_t value)
nuclear@0 192 {
nuclear@0 193 uint32_t ret;
nuclear@0 194
nuclear@0 195 asm volatile("1:\n\t"
nuclear@0 196 "lwarx %[r],0,%[i]\n\t"
nuclear@0 197 "cmpw 0,%[r],%[cmp]\n\t"
nuclear@0 198 "mfcr %[r]\n\t"
nuclear@0 199 "bne- 2f\n\t"
nuclear@0 200 "stwcx. %[val],0,%[i]\n\t"
nuclear@0 201 "bne- 1b\n\t"
nuclear@0 202 "2:\n"
nuclear@0 203 : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory");
nuclear@0 204
nuclear@0 205 return (ret & 0x20000000) ? 1 : 0;
nuclear@0 206 }
nuclear@0 207
nuclear@0 208 #elif defined(OVR_CPU_MIPS)
nuclear@0 209 typedef uint32_t T;
nuclear@0 210
nuclear@0 211 static inline uint32_t Exchange_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 212 {
nuclear@0 213 uint32_t ret;
nuclear@0 214
nuclear@0 215 asm volatile("1:\n\t"
nuclear@0 216 "ll %[r],0(%[i])\n\t"
nuclear@0 217 "sc %[j],0(%[i])\n\t"
nuclear@0 218 "beq %[j],$0,1b\n\t"
nuclear@0 219 "nop \n"
nuclear@0 220 : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
nuclear@0 221
nuclear@0 222 return ret;
nuclear@0 223 }
nuclear@0 224
nuclear@0 225 static inline uint32_t ExchangeAdd_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 226 {
nuclear@0 227 uint32_t ret;
nuclear@0 228
nuclear@0 229 asm volatile("1:\n\t"
nuclear@0 230 "ll %[r],0(%[i])\n\t"
nuclear@0 231 "addu %[j],%[r],%[j]\n\t"
nuclear@0 232 "sc %[j],0(%[i])\n\t"
nuclear@0 233 "beq %[j],$0,1b\n\t"
nuclear@0 234 "nop \n"
nuclear@0 235 : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
nuclear@0 236
nuclear@0 237 return ret;
nuclear@0 238 }
nuclear@0 239
nuclear@0 240 static inline bool CompareAndSet_NoSync(volatile uint32_t *i, uint32_t c, uint32_t value)
nuclear@0 241 {
nuclear@0 242 uint32_t ret, dummy;
nuclear@0 243
nuclear@0 244 asm volatile("1:\n\t"
nuclear@0 245 "move %[r],$0\n\t"
nuclear@0 246 "ll %[o],0(%[i])\n\t"
nuclear@0 247 "bne %[o],%[c],2f\n\t"
nuclear@0 248 "move %[r],%[v]\n\t"
nuclear@0 249 "sc %[r],0(%[i])\n\t"
nuclear@0 250 "beq %[r],$0,1b\n\t"
nuclear@0 251 "nop \n\t"
nuclear@0 252 "2:\n"
nuclear@0 253 : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value)
nuclear@0 254 : "cc", "memory");
nuclear@0 255
nuclear@0 256 return ret;
nuclear@0 257 }
nuclear@0 258
nuclear@0 259 #elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM)
nuclear@0 260 typedef uint32_t T;
nuclear@0 261
nuclear@0 262 static inline uint32_t Exchange_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 263 {
nuclear@0 264 for(;;)
nuclear@0 265 {
nuclear@0 266 T r = __ldrex(i);
nuclear@0 267 if (__strex(j, i) == 0)
nuclear@0 268 return r;
nuclear@0 269 }
nuclear@0 270 }
nuclear@0 271 static inline uint32_t ExchangeAdd_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 272 {
nuclear@0 273 for(;;)
nuclear@0 274 {
nuclear@0 275 T r = __ldrex(i);
nuclear@0 276 if (__strex(r + j, i) == 0)
nuclear@0 277 return r;
nuclear@0 278 }
nuclear@0 279 }
nuclear@0 280
nuclear@0 281 static inline bool CompareAndSet_NoSync(volatile uint32_t *i, uint32_t c, uint32_t value)
nuclear@0 282 {
nuclear@0 283 for(;;)
nuclear@0 284 {
nuclear@0 285 T r = __ldrex(i);
nuclear@0 286 if (r != c)
nuclear@0 287 return 0;
nuclear@0 288 if (__strex(value, i) == 0)
nuclear@0 289 return 1;
nuclear@0 290 }
nuclear@0 291 }
nuclear@0 292
nuclear@0 293 #elif defined(OVR_CPU_ARM)
nuclear@0 294 typedef uint32_t T;
nuclear@0 295
nuclear@0 296 static inline uint32_t Exchange_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 297 {
nuclear@0 298 uint32_t ret, dummy;
nuclear@0 299
nuclear@0 300 asm volatile("1:\n\t"
nuclear@0 301 "ldrex %[r],[%[i]]\n\t"
nuclear@0 302 "strex %[t],%[j],[%[i]]\n\t"
nuclear@0 303 "cmp %[t],#0\n\t"
nuclear@0 304 "bne 1b\n\t"
nuclear@0 305 : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
nuclear@0 306
nuclear@0 307 return ret;
nuclear@0 308 }
nuclear@0 309
nuclear@0 310 static inline uint32_t ExchangeAdd_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 311 {
nuclear@0 312 uint32_t ret, dummy, test;
nuclear@0 313
nuclear@0 314 asm volatile("1:\n\t"
nuclear@0 315 "ldrex %[r],[%[i]]\n\t"
nuclear@0 316 "add %[o],%[r],%[j]\n\t"
nuclear@0 317 "strex %[t],%[o],[%[i]]\n\t"
nuclear@0 318 "cmp %[t],#0\n\t"
nuclear@0 319 "bne 1b\n\t"
nuclear@0 320 : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
nuclear@0 321
nuclear@0 322 return ret;
nuclear@0 323 }
nuclear@0 324
nuclear@0 325 static inline bool CompareAndSet_NoSync(volatile uint32_t *i, uint32_t c, uint32_t value)
nuclear@0 326 {
nuclear@0 327 uint32_t ret = 1, dummy, test;
nuclear@0 328
nuclear@0 329 asm volatile("1:\n\t"
nuclear@0 330 "ldrex %[o],[%[i]]\n\t"
nuclear@0 331 "cmp %[o],%[c]\n\t"
nuclear@0 332 "bne 2f\n\t"
nuclear@0 333 "strex %[r],%[v],[%[i]]\n\t"
nuclear@0 334 "cmp %[r],#0\n\t"
nuclear@0 335 "bne 1b\n\t"
nuclear@0 336 "2:\n"
nuclear@0 337 : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value)
nuclear@0 338 : "cc", "memory");
nuclear@0 339
nuclear@0 340 return !ret;
nuclear@0 341 }
nuclear@0 342
nuclear@0 343 #elif defined(OVR_CPU_X86)
nuclear@0 344 typedef uint32_t T;
nuclear@0 345
nuclear@0 346 static inline uint32_t Exchange_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 347 {
nuclear@0 348 asm volatile("xchgl %1,%[i]\n"
nuclear@0 349 : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory");
nuclear@0 350
nuclear@0 351 return j;
nuclear@0 352 }
nuclear@0 353
nuclear@0 354 static inline uint32_t ExchangeAdd_NoSync(volatile uint32_t *i, uint32_t j)
nuclear@0 355 {
nuclear@0 356 asm volatile("lock; xaddl %1,%[i]\n"
nuclear@0 357 : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory");
nuclear@0 358
nuclear@0 359 return j;
nuclear@0 360 }
nuclear@0 361
nuclear@0 362 static inline bool CompareAndSet_NoSync(volatile uint32_t *i, uint32_t c, uint32_t value)
nuclear@0 363 {
nuclear@0 364 uint32_t ret;
nuclear@0 365
nuclear@0 366 asm volatile("lock; cmpxchgl %[v],%[i]\n"
nuclear@0 367 : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory");
nuclear@0 368
nuclear@0 369 return (ret == c);
nuclear@0 370 }
nuclear@0 371
nuclear@0 372 #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
nuclear@0 373
nuclear@0 374 typedef uint32_t T;
nuclear@0 375
nuclear@0 376 static inline T Exchange_NoSync(volatile T *i, T j)
nuclear@0 377 {
nuclear@0 378 T v;
nuclear@0 379 do {
nuclear@0 380 v = *i;
nuclear@0 381 } while (!__sync_bool_compare_and_swap(i, v, j));
nuclear@0 382 return v;
nuclear@0 383 }
nuclear@0 384
nuclear@0 385 static inline T ExchangeAdd_NoSync(volatile T *i, T j)
nuclear@0 386 {
nuclear@0 387 return __sync_fetch_and_add(i, j);
nuclear@0 388 }
nuclear@0 389
nuclear@0 390 static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
nuclear@0 391 {
nuclear@0 392 return __sync_bool_compare_and_swap(i, c, value);
nuclear@0 393 }
nuclear@0 394
nuclear@0 395 #endif // OS
nuclear@0 396 };
nuclear@0 397
nuclear@0 398
nuclear@0 399 // 8-Byte raw data data atomic op implementation class.
nuclear@0 400 // Currently implementation is provided only on systems with 64-bit pointers.
nuclear@0 401 struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase
nuclear@0 402 {
nuclear@0 403 #if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS)
nuclear@0 404
nuclear@0 405 // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
nuclear@0 406 typedef uint64_t T;
nuclear@0 407
nuclear@0 408 // *** Thread - Safe OS specific versions.
nuclear@0 409 #elif defined(OVR_OS_MS)
nuclear@0 410
nuclear@0 411 // This is only for 64-bit systems.
nuclear@0 412 typedef LONG64 T;
nuclear@0 413 typedef volatile T* InterlockTPtr;
nuclear@0 414 inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); }
nuclear@0 415 inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); }
nuclear@0 416 inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; }
nuclear@0 417
nuclear@0 418 #elif defined(OVR_CPU_PPC64)
nuclear@0 419
nuclear@0 420 typedef uint64_t T;
nuclear@0 421
nuclear@0 422 static inline uint64_t Exchange_NoSync(volatile uint64_t *i, uint64_t j)
nuclear@0 423 {
nuclear@0 424 uint64_t dummy, ret;
nuclear@0 425
nuclear@0 426 asm volatile("1:\n\t"
nuclear@0 427 "ldarx %[r],0,%[i]\n\t"
nuclear@0 428 "mr %[o],%[j]\n\t"
nuclear@0 429 "stdcx. %[o],0,%[i]\n\t"
nuclear@0 430 "bne- 1b\n"
nuclear@0 431 : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
nuclear@0 432
nuclear@0 433 return ret;
nuclear@0 434 }
nuclear@0 435
nuclear@0 436 static inline uint64_t ExchangeAdd_NoSync(volatile uint64_t *i, uint64_t j)
nuclear@0 437 {
nuclear@0 438 uint64_t dummy, ret;
nuclear@0 439
nuclear@0 440 asm volatile("1:\n\t"
nuclear@0 441 "ldarx %[r],0,%[i]\n\t"
nuclear@0 442 "add %[o],%[r],%[j]\n\t"
nuclear@0 443 "stdcx. %[o],0,%[i]\n\t"
nuclear@0 444 "bne- 1b\n"
nuclear@0 445 : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
nuclear@0 446
nuclear@0 447 return ret;
nuclear@0 448 }
nuclear@0 449
nuclear@0 450 static inline bool CompareAndSet_NoSync(volatile uint64_t *i, uint64_t c, uint64_t value)
nuclear@0 451 {
nuclear@0 452 uint64_t ret, dummy;
nuclear@0 453
nuclear@0 454 asm volatile("1:\n\t"
nuclear@0 455 "ldarx %[r],0,%[i]\n\t"
nuclear@0 456 "cmpw 0,%[r],%[cmp]\n\t"
nuclear@0 457 "mfcr %[r]\n\t"
nuclear@0 458 "bne- 2f\n\t"
nuclear@0 459 "stdcx. %[val],0,%[i]\n\t"
nuclear@0 460 "bne- 1b\n\t"
nuclear@0 461 "2:\n"
nuclear@0 462 : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc");
nuclear@0 463
nuclear@0 464 return (ret & 0x20000000) ? 1 : 0;
nuclear@0 465 }
nuclear@0 466
nuclear@0 467 #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
nuclear@0 468
nuclear@0 469 typedef uint64_t T;
nuclear@0 470
nuclear@0 471 static inline T Exchange_NoSync(volatile T *i, T j)
nuclear@0 472 {
nuclear@0 473 T v;
nuclear@0 474 do {
nuclear@0 475 v = *i;
nuclear@0 476 } while (!__sync_bool_compare_and_swap(i, v, j));
nuclear@0 477 return v;
nuclear@0 478 }
nuclear@0 479
nuclear@0 480 static inline T ExchangeAdd_NoSync(volatile T *i, T j)
nuclear@0 481 {
nuclear@0 482 return __sync_fetch_and_add(i, j);
nuclear@0 483 }
nuclear@0 484
nuclear@0 485 static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
nuclear@0 486 {
nuclear@0 487 return __sync_bool_compare_and_swap(i, c, value);
nuclear@0 488 }
nuclear@0 489
nuclear@0 490 #endif // OS
nuclear@0 491 };
nuclear@0 492
nuclear@0 493
nuclear@0 494 // Default implementation for AtomicOpsRaw; provides implementation of mem-fenced
nuclear@0 495 // atomic operations where fencing is done with a sync object wrapped around a NoSync
nuclear@0 496 // operation implemented in the base class. If such implementation is not possible
nuclear@0 497 // on a given platform, #ifdefs can be used to disable it and then op functions can be
nuclear@0 498 // implemented individually in the appropriate AtomicOpsRaw<size> class.
nuclear@0 499
nuclear@0 500 template<class O>
nuclear@0 501 struct AtomicOpsRaw_DefImpl : public O
nuclear@0 502 {
nuclear@0 503 typedef typename O::T O_T;
nuclear@0 504 typedef typename O::FullSync O_FullSync;
nuclear@0 505 typedef typename O::AcquireSync O_AcquireSync;
nuclear@0 506 typedef typename O::ReleaseSync O_ReleaseSync;
nuclear@0 507
nuclear@0 508 // If there is no thread support, provide the default implementation. In this case,
nuclear@0 509 // the base class (0) must still provide the T declaration.
nuclear@0 510 #ifndef OVR_ENABLE_THREADS
nuclear@0 511
nuclear@0 512 // Atomic exchange of val with argument. Returns old val.
nuclear@0 513 inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; }
nuclear@0 514 // Adds a new val to argument; returns its old val.
nuclear@0 515 inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; }
nuclear@0 516 // Compares the argument data with 'c' val.
nuclear@0 517 // If succeeded, stores val int '*p' and returns true; otherwise returns false.
nuclear@0 518 inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; }
nuclear@0 519
nuclear@0 520 #endif
nuclear@0 521
nuclear@0 522 // If NoSync wrapped implementation may not be possible, it this block should be
nuclear@0 523 // replaced with per-function implementation in O.
nuclear@0 524 // "AtomicOpsRaw_DefImpl<O>::" prefix in calls below.
nuclear@0 525 inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
nuclear@0 526 inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
nuclear@0 527 inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
nuclear@0 528 inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
nuclear@0 529 inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
nuclear@0 530 inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
nuclear@0 531 inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
nuclear@0 532 inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
nuclear@0 533 inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
nuclear@0 534
nuclear@0 535 // Loads and stores with memory fence. These have only the relevant versions.
nuclear@0 536 #ifdef OVR_CPU_X86
nuclear@0 537 // On X86, Store_Release is implemented as exchange. Note that we can also
nuclear@0 538 // consider 'sfence' in the future, although it is not as compatible with older CPUs.
nuclear@0 539 inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); }
nuclear@0 540 #else
nuclear@0 541 inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; }
nuclear@0 542 #endif
nuclear@0 543 inline static O_T Load_Acquire(const volatile O_T* p)
nuclear@0 544 {
nuclear@0 545 O_AcquireSync sync;
nuclear@0 546 OVR_UNUSED(sync);
nuclear@0 547
nuclear@0 548 #if defined(OVR_CC_MSVC)
nuclear@0 549 _ReadBarrier(); // Compiler fence and load barrier
nuclear@0 550 #elif defined(OVR_CC_INTEL)
nuclear@0 551 __memory_barrier(); // Compiler fence
nuclear@0 552 #else
nuclear@0 553 // GCC-compatible:
nuclear@0 554 asm volatile ("" : : : "memory"); // Compiler fence
nuclear@0 555 #endif
nuclear@0 556
nuclear@0 557 return *p;
nuclear@0 558 }
nuclear@0 559 };
nuclear@0 560
nuclear@0 561
nuclear@0 562 template<int size>
nuclear@0 563 struct AtomicOpsRaw : public AtomicOpsRawBase { };
nuclear@0 564
nuclear@0 565 template<>
nuclear@0 566 struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>
nuclear@0 567 {
nuclear@0 568 // Ensure that assigned type size is correct.
nuclear@0 569 AtomicOpsRaw()
nuclear@0 570 { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>::T) == 4); }
nuclear@0 571 };
nuclear@0 572 template<>
nuclear@0 573 struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>
nuclear@0 574 {
nuclear@0 575 AtomicOpsRaw()
nuclear@0 576 { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>::T) == 8); }
nuclear@0 577 };
nuclear@0 578
nuclear@0 579
nuclear@0 580 // *** AtomicOps - implementation of atomic Ops for specified class
nuclear@0 581
nuclear@0 582 // Implements atomic ops on a class, provided that the object is either
nuclear@0 583 // 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations
nuclear@0 584 // available). Relies on AtomicOpsRaw for much of implementation.
nuclear@0 585
nuclear@0 586 template<class C>
nuclear@0 587 class AtomicOps
nuclear@0 588 {
nuclear@0 589 typedef AtomicOpsRaw<sizeof(C)> Ops;
nuclear@0 590 typedef typename Ops::T T;
nuclear@0 591 typedef volatile typename Ops::T* PT;
nuclear@0 592 // We cast through unions to (1) avoid pointer size compiler warnings
nuclear@0 593 // and (2) ensure that there are no problems with strict pointer aliasing.
nuclear@0 594 union C2T_union { C c; T t; };
nuclear@0 595
nuclear@0 596 public:
nuclear@0 597 // General purpose implementation for standard syncs.
nuclear@0 598 inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; }
nuclear@0 599 inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; }
nuclear@0 600 inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; }
nuclear@0 601 inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; }
nuclear@0 602 inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; }
nuclear@0 603 inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; }
nuclear@0 604 inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; }
nuclear@0 605 inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; }
nuclear@0 606 inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); }
nuclear@0 607 inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); }
nuclear@0 608 inline static bool CompareAndSet_Acquire(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); }
nuclear@0 609 inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); }
nuclear@0 610
nuclear@0 611 // Loads and stores with memory fence. These have only the relevant versions.
nuclear@0 612 inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); }
nuclear@0 613 inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; }
nuclear@0 614
nuclear@0 615 // Deprecated typo error:
nuclear@0 616 inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); }
nuclear@0 617 };
nuclear@0 618
nuclear@0 619
nuclear@0 620
nuclear@0 621 // Atomic value base class - implements operations shared for integers and pointers.
nuclear@0 622 template<class T>
nuclear@0 623 class AtomicValueBase
nuclear@0 624 {
nuclear@0 625 protected:
nuclear@0 626 typedef AtomicOps<T> Ops;
nuclear@0 627 public:
nuclear@0 628
nuclear@0 629 volatile T Value;
nuclear@0 630
nuclear@0 631 inline AtomicValueBase() { }
nuclear@0 632 explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); }
nuclear@0 633
nuclear@0 634 // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire
nuclear@0 635 // here, since most algorithms do not require atomic loads. Needs some research.
nuclear@0 636 inline operator T() const { return Value; }
nuclear@0 637
nuclear@0 638 // *** Standard Atomic inlines
nuclear@0 639 inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); }
nuclear@0 640 inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); }
nuclear@0 641 inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); }
nuclear@0 642 inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); }
nuclear@0 643 inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); }
nuclear@0 644 inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); }
nuclear@0 645 inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Acquire(&Value, c, val); }
nuclear@0 646 inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); }
nuclear@0 647 // Load & Store.
nuclear@0 648 inline void Store_Release(T val) { Ops::Store_Release(&Value, val); }
nuclear@0 649 inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); }
nuclear@0 650 };
nuclear@0 651
nuclear@0 652
nuclear@0 653 // ***** AtomicPtr - Atomic pointer template
nuclear@0 654
nuclear@0 655 // This pointer class supports atomic assignments with release,
nuclear@0 656 // increment / decrement operations, and conditional compare + set.
nuclear@0 657
nuclear@0 658 template<class T>
nuclear@0 659 class AtomicPtr : public AtomicValueBase<T*>
nuclear@0 660 {
nuclear@0 661 typedef typename AtomicValueBase<T*>::Ops Ops;
nuclear@0 662
nuclear@0 663 public:
nuclear@0 664 // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor.
nuclear@0 665 inline AtomicPtr() : AtomicValueBase<T*>() { this->Value = 0; }
nuclear@0 666 explicit inline AtomicPtr(T* val) : AtomicValueBase<T*>(val) { }
nuclear@0 667
nuclear@0 668 // Pointer access.
nuclear@0 669 inline T* operator -> () const { return this->Load_Acquire(); }
nuclear@0 670
nuclear@0 671 // It looks like it is convenient to have Load_Acquire characteristics
nuclear@0 672 // for this, since that is convenient for algorithms such as linked
nuclear@0 673 // list traversals that can be added to bu another thread.
nuclear@0 674 inline operator T* () const { return this->Load_Acquire(); }
nuclear@0 675
nuclear@0 676
nuclear@0 677 // *** Standard Atomic inlines (applicable to pointers)
nuclear@0 678
nuclear@0 679 // ExhangeAdd considers pointer size for pointers.
nuclear@0 680 template<class I>
nuclear@0 681 inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); }
nuclear@0 682 template<class I>
nuclear@0 683 inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); }
nuclear@0 684 template<class I>
nuclear@0 685 inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); }
nuclear@0 686 template<class I>
nuclear@0 687 inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); }
nuclear@0 688
nuclear@0 689 // *** Atomic Operators
nuclear@0 690
nuclear@0 691 inline T* operator = (T* val) { this->Store_Release(val); return val; }
nuclear@0 692
nuclear@0 693 template<class I>
nuclear@0 694 inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; }
nuclear@0 695 template<class I>
nuclear@0 696 inline T* operator -= (I val) { return operator += (-val); }
nuclear@0 697
nuclear@0 698 inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; }
nuclear@0 699 inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; }
nuclear@0 700 inline T* operator ++ (int) { return ExchangeAdd_Sync(1); }
nuclear@0 701 inline T* operator -- (int) { return ExchangeAdd_Sync(-1); }
nuclear@0 702 };
nuclear@0 703
nuclear@0 704
nuclear@0 705 // ***** AtomicInt - Atomic integer template
nuclear@0 706
nuclear@0 707 // Implements an atomic integer type; the exact type to use is provided
nuclear@0 708 // as an argument. Supports atomic Acquire / Release semantics, atomic
nuclear@0 709 // arithmetic operations, and atomic conditional compare + set.
nuclear@0 710
nuclear@0 711 template<class T>
nuclear@0 712 class AtomicInt : public AtomicValueBase<T>
nuclear@0 713 {
nuclear@0 714 typedef typename AtomicValueBase<T>::Ops Ops;
nuclear@0 715
nuclear@0 716 public:
nuclear@0 717 inline AtomicInt() : AtomicValueBase<T>() { }
nuclear@0 718 explicit inline AtomicInt(T val) : AtomicValueBase<T>(val) { }
nuclear@0 719
nuclear@0 720
nuclear@0 721 // *** Standard Atomic inlines (applicable to int)
nuclear@0 722 inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); }
nuclear@0 723 inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); }
nuclear@0 724 inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); }
nuclear@0 725 inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); }
nuclear@0 726 // These increments could be more efficient because they don't return a value.
nuclear@0 727 inline void Increment_Sync() { ExchangeAdd_Sync((T)1); }
nuclear@0 728 inline void Increment_Release() { ExchangeAdd_Release((T)1); }
nuclear@0 729 inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); }
nuclear@0 730 inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); }
nuclear@0 731
nuclear@0 732 // *** Atomic Operators
nuclear@0 733
nuclear@0 734 inline T operator = (T val) { this->Store_Release(val); return val; }
nuclear@0 735 inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; }
nuclear@0 736 inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; }
nuclear@0 737
nuclear@0 738 inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; }
nuclear@0 739 inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; }
nuclear@0 740 inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); }
nuclear@0 741 inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); }
nuclear@0 742
nuclear@0 743 // More complex atomic operations. Leave it to compiler whether to optimize them or not.
nuclear@0 744 T operator &= (T arg)
nuclear@0 745 {
nuclear@0 746 T comp, newVal;
nuclear@0 747 do {
nuclear@0 748 comp = this->Value;
nuclear@0 749 newVal = comp & arg;
nuclear@0 750 } while(!this->CompareAndSet_Sync(comp, newVal));
nuclear@0 751 return newVal;
nuclear@0 752 }
nuclear@0 753
nuclear@0 754 T operator |= (T arg)
nuclear@0 755 {
nuclear@0 756 T comp, newVal;
nuclear@0 757 do {
nuclear@0 758 comp = this->Value;
nuclear@0 759 newVal = comp | arg;
nuclear@0 760 } while(!this->CompareAndSet_Sync(comp, newVal));
nuclear@0 761 return newVal;
nuclear@0 762 }
nuclear@0 763
nuclear@0 764 T operator ^= (T arg)
nuclear@0 765 {
nuclear@0 766 T comp, newVal;
nuclear@0 767 do {
nuclear@0 768 comp = this->Value;
nuclear@0 769 newVal = comp ^ arg;
nuclear@0 770 } while(!this->CompareAndSet_Sync(comp, newVal));
nuclear@0 771 return newVal;
nuclear@0 772 }
nuclear@0 773
nuclear@0 774 T operator *= (T arg)
nuclear@0 775 {
nuclear@0 776 T comp, newVal;
nuclear@0 777 do {
nuclear@0 778 comp = this->Value;
nuclear@0 779 newVal = comp * arg;
nuclear@0 780 } while(!this->CompareAndSet_Sync(comp, newVal));
nuclear@0 781 return newVal;
nuclear@0 782 }
nuclear@0 783
nuclear@0 784 T operator /= (T arg)
nuclear@0 785 {
nuclear@0 786 T comp, newVal;
nuclear@0 787 do {
nuclear@0 788 comp = this->Value;
nuclear@0 789 newVal = comp / arg;
nuclear@0 790 } while(!CompareAndSet_Sync(comp, newVal));
nuclear@0 791 return newVal;
nuclear@0 792 }
nuclear@0 793
nuclear@0 794 T operator >>= (unsigned bits)
nuclear@0 795 {
nuclear@0 796 T comp, newVal;
nuclear@0 797 do {
nuclear@0 798 comp = this->Value;
nuclear@0 799 newVal = comp >> bits;
nuclear@0 800 } while(!CompareAndSet_Sync(comp, newVal));
nuclear@0 801 return newVal;
nuclear@0 802 }
nuclear@0 803
nuclear@0 804 T operator <<= (unsigned bits)
nuclear@0 805 {
nuclear@0 806 T comp, newVal;
nuclear@0 807 do {
nuclear@0 808 comp = this->Value;
nuclear@0 809 newVal = comp << bits;
nuclear@0 810 } while(!this->CompareAndSet_Sync(comp, newVal));
nuclear@0 811 return newVal;
nuclear@0 812 }
nuclear@0 813 };
nuclear@0 814
nuclear@0 815
nuclear@0 816 //-----------------------------------------------------------------------------------
nuclear@0 817 // ***** Lock
nuclear@0 818
nuclear@0 819 // Lock is a simplest and most efficient mutual-exclusion lock class.
nuclear@0 820 // Unlike Mutex, it cannot be waited on.
nuclear@0 821
nuclear@0 822 class Lock
nuclear@0 823 {
nuclear@0 824 // NOTE: Locks are not allocatable and they themselves should not allocate
nuclear@0 825 // memory by standard means. This is the case because StandardAllocator
nuclear@0 826 // relies on this class.
nuclear@0 827 // Make 'delete' private. Don't do this for 'new' since it can be redefined.
nuclear@0 828 void operator delete(void*) {}
nuclear@0 829
nuclear@0 830
nuclear@0 831 // *** Lock implementation for various platforms.
nuclear@0 832
nuclear@0 833 #if !defined(OVR_ENABLE_THREADS)
nuclear@0 834
nuclear@0 835 public:
nuclear@0 836 // With no thread support, lock does nothing.
nuclear@0 837 inline Lock() { }
nuclear@0 838 inline Lock(unsigned) { }
nuclear@0 839 inline ~Lock() { }
nuclear@0 840 inline void DoLock() { }
nuclear@0 841 inline void Unlock() { }
nuclear@0 842
nuclear@0 843 // Windows.
nuclear@0 844 #elif defined(OVR_OS_MS)
nuclear@0 845
nuclear@0 846 CRITICAL_SECTION cs;
nuclear@0 847 public:
nuclear@0 848 Lock(unsigned spinCount = 10000); // Mutexes with non-zero spin counts usually result in better performance.
nuclear@0 849 ~Lock();
nuclear@0 850 // Locking functions.
nuclear@0 851 inline void DoLock() { ::EnterCriticalSection(&cs); }
nuclear@0 852 inline void Unlock() { ::LeaveCriticalSection(&cs); }
nuclear@0 853
nuclear@0 854 #else
nuclear@0 855 pthread_mutex_t mutex;
nuclear@0 856
nuclear@0 857 public:
nuclear@0 858 static pthread_mutexattr_t RecursiveAttr;
nuclear@0 859 static bool RecursiveAttrInit;
nuclear@0 860
nuclear@0 861 Lock (unsigned spinCount = 0) // To do: Support spin count, probably via a custom lock implementation.
nuclear@0 862 {
nuclear@0 863 OVR_UNUSED(spinCount);
nuclear@0 864 if (!RecursiveAttrInit)
nuclear@0 865 {
nuclear@0 866 pthread_mutexattr_init(&RecursiveAttr);
nuclear@0 867 pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
nuclear@0 868 RecursiveAttrInit = 1;
nuclear@0 869 }
nuclear@0 870 pthread_mutex_init(&mutex,&RecursiveAttr);
nuclear@0 871 }
nuclear@0 872 ~Lock () { pthread_mutex_destroy(&mutex); }
nuclear@0 873 inline void DoLock() { pthread_mutex_lock(&mutex); }
nuclear@0 874 inline void Unlock() { pthread_mutex_unlock(&mutex); }
nuclear@0 875
nuclear@0 876 #endif // OVR_ENABLE_THREDS
nuclear@0 877
nuclear@0 878
nuclear@0 879 public:
nuclear@0 880 // Locker class, used for automatic locking
nuclear@0 881 class Locker
nuclear@0 882 {
nuclear@0 883 public:
nuclear@0 884 Lock *pLock;
nuclear@0 885 inline Locker(Lock *plock)
nuclear@0 886 { pLock = plock; pLock->DoLock(); }
nuclear@0 887 inline ~Locker()
nuclear@0 888 { pLock->Unlock(); }
nuclear@0 889 };
nuclear@0 890 };
nuclear@0 891
nuclear@0 892
nuclear@0 893 //-------------------------------------------------------------------------------------
nuclear@0 894 // Globally shared Lock implementation used for MessageHandlers, etc.
nuclear@0 895
nuclear@0 896 class SharedLock
nuclear@0 897 {
nuclear@0 898 public:
nuclear@0 899 SharedLock() : UseCount(0) {}
nuclear@0 900
nuclear@0 901 Lock* GetLockAddRef();
nuclear@0 902 void ReleaseLock(Lock* plock);
nuclear@0 903
nuclear@0 904 private:
nuclear@0 905 Lock* toLock() { return (Lock*)Buffer; }
nuclear@0 906
nuclear@0 907 // UseCount and max alignment.
nuclear@0 908 volatile int UseCount;
nuclear@0 909 uint64_t Buffer[(sizeof(Lock)+sizeof(uint64_t)-1)/sizeof(uint64_t)];
nuclear@0 910 };
nuclear@0 911
nuclear@0 912
nuclear@0 913 } // OVR
nuclear@0 914
nuclear@0 915 #endif