summaryrefslogtreecommitdiff
path: root/Runtime/Threads/AtomicOps.h
blob: a166cf408badb37d4e19a4595efdf1635ab70282 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#ifndef UNITY_ATOMIC_OPS_HPP_
#define UNITY_ATOMIC_OPS_HPP_

// Primitive atomic instructions as defined by the CPU architecture.

#include "Runtime/Allocator/MemoryMacros.h"	// defines FORCE_INLINE (?!)

// AtomicAdd - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
FORCE_INLINE int AtomicAdd (int volatile* i, int value);

// AtomicSub - Returns the new value, after the operation has been performed (as defined by OSAtomicSub32Barrier)
FORCE_INLINE int AtomicSub (int volatile* i, int value);

// AtomicIncrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
FORCE_INLINE int AtomicIncrement (int volatile* i);

// AtomicDecrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
FORCE_INLINE int AtomicDecrement (int volatile* i);

// AtomicCompareExchange - Returns value is the initial value of the Destination pointer (as defined by _InterlockedCompareExchange)
FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue);

// AtomicExchange - Returns the initial value pointed to by Target (as defined by _InterlockedExchange)
FORCE_INLINE int AtomicExchange (int volatile* i, int value);

#define ATOMIC_API_GENERIC (UNITY_OSX || UNITY_IPHONE || UNITY_WIN || UNITY_XENON || UNITY_PS3 || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_WII || UNITY_TIZEN)

#if !ATOMIC_API_GENERIC && SUPPORT_THREADS
#	include "PlatformAtomicOps.h"
#else

// This file contains implementations for the platforms we already support.
// Going forward (some of) these implementations must move to the platform specific directories.

#include "Runtime/Utilities/Utility.h"

#if UNITY_WIN
#include <intrin.h>
typedef char UnityAtomicsTypesAssert_FailsIfIntSize_NEQ_LongSize[sizeof(int) == sizeof(LONG) ? 1 : -1];
#elif UNITY_OSX



#include <libkern/OSAtomic.h>

#elif UNITY_IPHONE
#include <libkern/OSAtomic.h>
#elif UNITY_PS3
#include <cell/atomic.h>
#elif UNITY_ANDROID || (UNITY_LINUX && defined(__GNUC__) && defined(__arm__))
// use gcc builtin __sync_*
#elif UNITY_BB10
#include <atomic.h>
#include <arm/smpxchg.h>
#endif

#include "Runtime/Utilities/Utility.h"

// AtomicAdd - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
FORCE_INLINE int AtomicAdd (int volatile* i, int value) {
#if UNITY_OSX || UNITY_IPHONE
#if defined(__ppc__)
	#error "Atomic addition undefined for this platform"
#endif

	return OSAtomicAdd32Barrier (value, (int*)i);

#elif UNITY_WIN || UNITY_XENON
	return _InterlockedExchangeAdd ((long volatile*)i, value) + value;
#elif UNITY_PS3
	return cellAtomicAdd32((uint32_t*)i, value) + value;	// on ps3 it returns the pre-increment value
#elif UNITY_ANDROID || UNITY_TIZEN
	return __sync_add_and_fetch(i,value);
#elif UNITY_PEPPER
	int temp = value;
	__asm__ __volatile__("lock; xaddl %0,%1"
						 : "+r" (temp), "+m" (*i)
						 : : "memory");
	// temp now holds the old value of *ptr
	//	if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug)
	__asm__ __volatile__("lfence" : : : "memory");
	return temp + value;
#elif UNITY_LINUX
	return __sync_add_and_fetch(i, value);
#elif UNITY_BB10
	return atomic_add_value((unsigned int*)i, value)+value;
#elif UNITY_WII
	int wasEnabled = OSDisableInterrupts();
	*i = (*i) + value;
	OSRestoreInterrupts(wasEnabled);
	return *i;
#elif !SUPPORT_THREADS
	return *i+=value;
#else
#error "Atomic op undefined for this platform"
#endif
}

// AtomicSub - Returns the new value, after the operation has been performed (as defined by OSAtomicSub32Barrier)
inline int AtomicSub (int volatile* i, int value) {
#if UNITY_PS3
	return cellAtomicSub32((uint32_t*)i, value) - value;	// on ps3 it returns the pre-increment value
#elif UNITY_ANDROID || UNITY_TIZEN
	return __sync_sub_and_fetch(i,value);
#elif UNITY_LINUX
	return __sync_sub_and_fetch(i, value);
#elif UNITY_BB10
	return atomic_sub_value((unsigned int*)i, value) - value;
#else
	return AtomicAdd(i, -value);
#endif
}

// AtomicIncrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
FORCE_INLINE int AtomicIncrement (int volatile* i) {
#if UNITY_WIN || UNITY_XENON
	return _InterlockedIncrement ((long volatile*)i);
#elif UNITY_PS3
	return cellAtomicIncr32((uint32_t*)i)+1;	// on ps3 it returns the pre-increment value
#elif !SUPPORT_THREADS
	return ++*i;
#else
	return AtomicAdd(i, 1);
#endif
}

// AtomicDecrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
FORCE_INLINE int AtomicDecrement (int volatile* i) {
#if UNITY_WIN || UNITY_XENON
	return _InterlockedDecrement ((long volatile*)i);
#elif UNITY_PS3
	return cellAtomicDecr32((uint32_t*)i)-1;	// on ps3 it returns the pre-increment value
#elif !SUPPORT_THREADS
	return --*i;
#else
	return AtomicSub(i, 1);
#endif
}

// AtomicCompareExchange - Returns value is the initial value of the Destination pointer (as defined by _InterlockedCompareExchange)
#if UNITY_WIN || UNITY_XENON
FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
	return _InterlockedCompareExchange ((long volatile*)i, (long)newValue, (long)expectedValue) == expectedValue;
}
#elif UNITY_OSX || UNITY_IPHONE
FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
	return OSAtomicCompareAndSwap32Barrier (expectedValue, newValue, reinterpret_cast<volatile int32_t*>(i));
}
#elif UNITY_LINUX || UNITY_PEPPER || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
#	if UNITY_BB10
	return _smp_cmpxchg((unsigned int*)i, expectedValue, newValue) == expectedValue;
#	else
	return __sync_bool_compare_and_swap(i, expectedValue, newValue);
#endif
}
#elif UNITY_PS3
FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
	return cellAtomicCompareAndSwap32((uint32_t*)i, (uint32_t)expectedValue, (uint32_t)newValue) == (uint32_t)expectedValue;
}
#endif

// AtomicExchange - Returns the initial value pointed to by Target (as defined by _InterlockedExchange)
#if UNITY_WIN || UNITY_XENON
FORCE_INLINE int AtomicExchange (int volatile* i, int value) {
	return (int)_InterlockedExchange ((long volatile*)i, (long)value);
}
#elif UNITY_ANDROID || UNITY_TIZEN || UNITY_OSX || UNITY_LINUX // fallback to loop
FORCE_INLINE int AtomicExchange (int volatile* i, int value) {
	int prev;
	do { prev = *i; }
	while (!AtomicCompareExchange(i, value, prev));
	return prev;
}
#endif

#endif // ATOMIC_API_GENERIC
#undef ATOMIC_API_GENERIC

#endif //UNITY_ATOMIC_OPS_HPP_