alternative new version of the AppleUtility library
[ardour.git] / libs / appleutility / CoreAudio / PublicUtility / CAAtomic.h
1 /*
2      File: CAAtomic.h
3  Abstract: Part of CoreAudio Utility Classes
4   Version: 1.1
5  
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12  
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28  
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34  
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43  
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45  
46 */
47 /*
48         This file implements all Atomic operations using Interlocked functions specified in
49         Winbase.h
50 NOTE: According to Microsoft documentation, all Interlocked functions generates a
51 full barrier. 
52         On Windows:
53         As the Interlocked functions returns the Old value, Extra checks and operations 
54         are made after the atomic operation to return value consistent with OSX counterparts.
55 */
56 #ifndef __CAAtomic_h__
57 #define __CAAtomic_h__
58
59 #if TARGET_OS_WIN32
60         #include <windows.h>
61         #include <intrin.h>
62         #pragma intrinsic(_InterlockedOr)\r
63         #pragma intrinsic(_InterlockedAnd)
64 #else
65         #include <CoreFoundation/CFBase.h>
66         #include <libkern/OSAtomic.h>
67 #endif
68
69 inline void CAMemoryBarrier() 
70 {
71 #if TARGET_OS_WIN32
72         MemoryBarrier();
73 #else
74         OSMemoryBarrier();
75 #endif
76 }
77
78 inline SInt32 CAAtomicAdd32Barrier(SInt32 theAmt, volatile SInt32* theValue)
79 {
80 #if TARGET_OS_WIN32
81         long lRetVal = InterlockedExchangeAdd((volatile long*)theValue, theAmt);
82         // InterlockedExchangeAdd returns the original value which differs from OSX version. 
83         // At this point the addition would have occured and hence returning the new value
84         // to keep it sync with OSX.
85         return lRetVal + theAmt;
86 #else
87         return OSAtomicAdd32Barrier(theAmt, (volatile int32_t *)theValue);
88 #endif
89 }
90
91 inline SInt32 CAAtomicOr32Barrier(UInt32 theMask, volatile UInt32* theValue)
92 {
93 #if TARGET_OS_WIN32
94         // InterlockedAnd macro is not defined in x86 platform, and hence using the intrinsic
95         // function instead.
96         long j = _InterlockedOr((volatile long*)theValue, theMask);
97         // _InterlockedOr returns the original value which differs from OSX version.
98         // Returning the new value similar to OSX
99         return (SInt32)(j | theMask);
100 #else
101         return OSAtomicOr32Barrier(theMask, (volatile uint32_t *)theValue);
102 #endif
103 }
104
105 inline SInt32 CAAtomicAnd32Barrier(UInt32 theMask, volatile UInt32* theValue)
106 {
107 #if TARGET_OS_WIN32
108 // InterlockedAnd macro is not defined in x86 platform, and hence using the intrinsic
109 // function instead.
110         long j = _InterlockedAnd((volatile long*)theValue, theMask);
111         // _InterlockedAnd returns the original value which differs from OSX version.
112         // Returning the new value similar to OSX
113         return (SInt32)(j & theMask);
114 #else
115         return OSAtomicAnd32Barrier(theMask, (volatile uint32_t *)theValue);
116 #endif
117 }
118
119 inline bool CAAtomicCompareAndSwap32Barrier(SInt32 oldValue, SInt32 newValue, volatile SInt32 *theValue)
120 {
121 #if TARGET_OS_WIN32
122         // InterlockedCompareExchange returns the old value. But we need to return bool value.
123         long lRetVal = InterlockedCompareExchange((volatile long*)theValue, newValue, oldValue);
124 // Hence we check if the new value is set and if it is we return true else false.
125 // If theValue is equal to oldValue then the swap happens. Otherwise swap doesn't happen.
126         return (oldValue == lRetVal);
127 #else
128         return OSAtomicCompareAndSwap32Barrier(oldValue, newValue, (volatile int32_t *)theValue);
129 #endif
130 }
131
132
133 inline SInt32 CAAtomicIncrement32(volatile SInt32* theValue)
134 {
135 #if TARGET_OS_WIN32
136         return (SInt32)InterlockedIncrement((volatile long*)theValue);
137 #else
138         return OSAtomicIncrement32((volatile int32_t *)theValue);
139 #endif
140 }
141
142 inline SInt32 CAAtomicDecrement32(volatile SInt32* theValue)
143 {
144 #if TARGET_OS_WIN32
145         return (SInt32)InterlockedDecrement((volatile long*)theValue);
146 #else
147         return OSAtomicDecrement32((volatile int32_t *)theValue);
148 #endif
149 }
150
151 inline SInt32 CAAtomicIncrement32Barrier(volatile SInt32* theValue)
152 {
153 #if TARGET_OS_WIN32
154         return CAAtomicIncrement32(theValue);
155 #else
156         return OSAtomicIncrement32Barrier((volatile int32_t *)theValue);
157 #endif
158 }
159
160 inline SInt32 CAAtomicDecrement32Barrier(volatile SInt32* theValue)
161 {
162 #if TARGET_OS_WIN32
163         return CAAtomicDecrement32(theValue);
164 #else
165         return OSAtomicDecrement32Barrier((volatile int32_t *)theValue);
166 #endif
167 }
168
169 inline bool CAAtomicTestAndClearBarrier(int bitToClear, void* theAddress)
170 {
171 #if TARGET_OS_WIN32
172         BOOL bOldVal = InterlockedBitTestAndReset((long*)theAddress, bitToClear);
173         return (bOldVal ? true : false);
174 #else
175         return OSAtomicTestAndClearBarrier(bitToClear, (volatile void *)theAddress);
176 #endif
177 }
178
179 inline bool CAAtomicTestAndClear(int bitToClear, void* theAddress)
180 {
181 #if TARGET_OS_WIN32
182         BOOL bOldVal = CAAtomicTestAndClearBarrier(bitToClear, (long*)theAddress);
183         return (bOldVal ? true : false);
184 #else
185         return OSAtomicTestAndClear(bitToClear, (volatile void *)theAddress);
186 #endif
187 }
188
189 inline bool CAAtomicTestAndSetBarrier(int bitToSet, void* theAddress)
190 {
191 #if TARGET_OS_WIN32
192         BOOL bOldVal = InterlockedBitTestAndSet((long*)theAddress, bitToSet);
193         return (bOldVal ? true : false);
194 #else
195         return OSAtomicTestAndSetBarrier(bitToSet, (volatile void *)theAddress);
196 #endif
197 }
198
199 // int32_t flavors -- for C++ only since we can't overload in C
200 // CFBase.h defines SInt32 as signed int which is similar to int32_t. If CFBase.h is included, then
201 // this will generate redefinition error. But on Mac, CFBase.h, still includes MacTypes.h where
202 // SInt32 is defined as signed long so this would work there.
203 // So in order to fix the redefinition errors, we define these functions only if MacTypes.h is included.
204 #if defined(__cplusplus) && defined(__MACTYPES__) && !__LP64__
205 inline int32_t CAAtomicAdd32Barrier(int32_t theAmt, volatile int32_t* theValue)
206 {
207         return CAAtomicAdd32Barrier(theAmt, (volatile SInt32 *)theValue);
208 }
209
210 inline int32_t CAAtomicOr32Barrier(uint32_t theMask, volatile uint32_t* theValue)
211 {
212         return CAAtomicOr32Barrier(theMask, (volatile UInt32 *)theValue);
213 }
214
215 inline int32_t CAAtomicAnd32Barrier(uint32_t theMask, volatile uint32_t* theValue)
216 {
217         return CAAtomicAnd32Barrier(theMask, (volatile UInt32 *)theValue);
218 }
219
220 inline bool CAAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue)
221 {
222         return CAAtomicCompareAndSwap32Barrier(oldValue, newValue, (volatile SInt32 *)theValue);
223 }
224
225 inline int32_t CAAtomicIncrement32(volatile int32_t* theValue)
226 {
227         return CAAtomicIncrement32((volatile SInt32 *)theValue);
228 }
229
230 inline int32_t CAAtomicDecrement32(volatile int32_t* theValue)
231 {
232         return CAAtomicDecrement32((volatile SInt32 *)theValue);
233 }
234
235 inline int32_t CAAtomicIncrement32Barrier(volatile int32_t* theValue)
236 {
237         return CAAtomicIncrement32Barrier((volatile SInt32 *)theValue);
238 }
239
240 inline int32_t CAAtomicDecrement32Barrier(volatile int32_t* theValue)
241 {
242         return CAAtomicDecrement32Barrier((volatile SInt32 *)theValue);
243 }
244 #endif // __cplusplus && !__LP64__
245
246 #if __LP64__
247 inline bool CAAtomicCompareAndSwap64Barrier( int64_t __oldValue, int64_t __newValue, volatile int64_t *__theValue )
248 {
249         return OSAtomicCompareAndSwap64Barrier(__oldValue, __newValue, __theValue);
250 }
251 #endif
252
253 inline bool CAAtomicCompareAndSwapPtrBarrier(void *__oldValue, void *__newValue, volatile void ** __theValue)
254 {
255 #if __LP64__
256         return CAAtomicCompareAndSwap64Barrier((int64_t)__oldValue, (int64_t)__newValue, (int64_t *)__theValue);
257 #else
258         return CAAtomicCompareAndSwap32Barrier((int32_t)__oldValue, (int32_t)__newValue, (int32_t *)__theValue);
259 #endif
260 }
261
262 /* Spinlocks.  These use memory barriers as required to synchronize access to shared
263  * memory protected by the lock.  The lock operation spins, but employs various strategies
264  * to back off if the lock is held, making it immune to most priority-inversion livelocks.
265  * The try operation immediately returns false if the lock was held, true if it took the
266  * lock.  The convention is that unlocked is zero, locked is nonzero.
267  */
268 #define CA_SPINLOCK_INIT    0
269
270 typedef int32_t CASpinLock;
271
272 bool    CASpinLockTry( volatile CASpinLock *__lock );
273 void    CASpinLockLock( volatile CASpinLock *__lock );
274 void    CASpinLockUnlock( volatile CASpinLock *__lock );
275
276 inline void    CASpinLockLock( volatile CASpinLock *__lock )
277 {
278 #if TARGET_OS_MAC
279         OSSpinLockLock(__lock);
280 #else
281         while (CAAtomicTestAndSetBarrier(0, (void*)__lock))
282                 usleep(1000); // ???
283 #endif
284 }
285
286 inline void    CASpinLockUnlock( volatile CASpinLock *__lock )
287 {
288 #if TARGET_OS_MAC
289         OSSpinLockUnlock(__lock);
290 #else
291         CAAtomicTestAndClearBarrier(0, (void*)__lock);
292 #endif
293 }
294
295 inline bool    CASpinLockTry( volatile CASpinLock *__lock )
296 {
297 #if TARGET_OS_MAC
298         return OSSpinLockTry(__lock);
299 #else
300         return (CAAtomicTestAndSetBarrier(0, (void*)__lock) == 0);
301 #endif
302 }
303
304
305 #endif // __CAAtomic_h__