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
|