1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4// __forceinline implementation of the Interlocked class methods
5//
6
7#ifndef __GCENV_INTERLOCKED_INL__
8#define __GCENV_INTERLOCKED_INL__
9
10#ifdef _MSC_VER
11#include <intrin.h>
12#endif // _MSC_VER
13
14#ifndef _MSC_VER
15__forceinline void Interlocked::ArmInterlockedOperationBarrier()
16{
17#ifdef _ARM64_
18 // See PAL_ArmInterlockedOperationBarrier() in the PAL
19 __sync_synchronize();
20#endif // _ARM64_
21}
22#endif // !_MSC_VER
23
24// Increment the value of the specified 32-bit variable as an atomic operation.
25// Parameters:
26// addend - variable to be incremented
27// Return:
28// The resulting incremented value
29template <typename T>
30__forceinline T Interlocked::Increment(T volatile *addend)
31{
32#ifdef _MSC_VER
33 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
34 return _InterlockedIncrement((long*)addend);
35#else
36 T result = __sync_add_and_fetch(addend, 1);
37 ArmInterlockedOperationBarrier();
38 return result;
39#endif
40}
41
42// Decrement the value of the specified 32-bit variable as an atomic operation.
43// Parameters:
44// addend - variable to be decremented
45// Return:
46// The resulting decremented value
47template <typename T>
48__forceinline T Interlocked::Decrement(T volatile *addend)
49{
50#ifdef _MSC_VER
51 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
52 return _InterlockedDecrement((long*)addend);
53#else
54 T result = __sync_sub_and_fetch(addend, 1);
55 ArmInterlockedOperationBarrier();
56 return result;
57#endif
58}
59
60// Set a 32-bit variable to the specified value as an atomic operation.
61// Parameters:
62// destination - value to be exchanged
63// value - value to set the destination to
64// Return:
65// The previous value of the destination
66template <typename T>
67__forceinline T Interlocked::Exchange(T volatile *destination, T value)
68{
69#ifdef _MSC_VER
70 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
71 return _InterlockedExchange((long*)destination, value);
72#else
73 T result = __sync_swap(destination, value);
74 ArmInterlockedOperationBarrier();
75 return result;
76#endif
77}
78
79// Performs an atomic compare-and-exchange operation on the specified values.
80// Parameters:
81// destination - value to be exchanged
82// exchange - value to set the destinaton to
83// comparand - value to compare the destination to before setting it to the exchange.
84// The destination is set only if the destination is equal to the comparand.
85// Return:
86// The original value of the destination
87template <typename T>
88__forceinline T Interlocked::CompareExchange(T volatile *destination, T exchange, T comparand)
89{
90#ifdef _MSC_VER
91 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
92 return _InterlockedCompareExchange((long*)destination, exchange, comparand);
93#else
94 T result = __sync_val_compare_and_swap(destination, comparand, exchange);
95 ArmInterlockedOperationBarrier();
96 return result;
97#endif
98}
99
100// Perform an atomic addition of two 32-bit values and return the original value of the addend.
101// Parameters:
102// addend - variable to be added to
103// value - value to add
104// Return:
105// The previous value of the addend
106template <typename T>
107__forceinline T Interlocked::ExchangeAdd(T volatile *addend, T value)
108{
109#ifdef _MSC_VER
110 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
111 return _InterlockedExchangeAdd((long*)addend, value);
112#else
113 T result = __sync_fetch_and_add(addend, value);
114 ArmInterlockedOperationBarrier();
115 return result;
116#endif
117}
118
119// Perform an atomic AND operation on the specified values values
120// Parameters:
121// destination - the first operand and the destination
122// value - second operand
123template <typename T>
124__forceinline void Interlocked::And(T volatile *destination, T value)
125{
126#ifdef _MSC_VER
127 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
128 _InterlockedAnd((long*)destination, value);
129#else
130 __sync_and_and_fetch(destination, value);
131 ArmInterlockedOperationBarrier();
132#endif
133}
134
135// Perform an atomic OR operation on the specified values values
136// Parameters:
137// destination - the first operand and the destination
138// value - second operand
139template <typename T>
140__forceinline void Interlocked::Or(T volatile *destination, T value)
141{
142#ifdef _MSC_VER
143 static_assert(sizeof(long) == sizeof(T), "Size of long must be the same as size of T");
144 _InterlockedOr((long*)destination, value);
145#else
146 __sync_or_and_fetch(destination, value);
147 ArmInterlockedOperationBarrier();
148#endif
149}
150
151// Set a pointer variable to the specified value as an atomic operation.
152// Parameters:
153// destination - value to be exchanged
154// value - value to set the destination to
155// Return:
156// The previous value of the destination
157template <typename T>
158__forceinline T Interlocked::ExchangePointer(T volatile * destination, T value)
159{
160#ifdef _MSC_VER
161#ifdef BIT64
162 return (T)(TADDR)_InterlockedExchangePointer((void* volatile *)destination, value);
163#else
164 return (T)(TADDR)_InterlockedExchange((long volatile *)(void* volatile *)destination, (long)(void*)value);
165#endif
166#else
167 T result = (T)(TADDR)__sync_swap((void* volatile *)destination, value);
168 ArmInterlockedOperationBarrier();
169 return result;
170#endif
171}
172
173template <typename T>
174__forceinline T Interlocked::ExchangePointer(T volatile * destination, std::nullptr_t value)
175{
176#ifdef _MSC_VER
177#ifdef BIT64
178 return (T)(TADDR)_InterlockedExchangePointer((void* volatile *)destination, value);
179#else
180 return (T)(TADDR)_InterlockedExchange((long volatile *)(void* volatile *)destination, (long)(void*)value);
181#endif
182#else
183 T result = (T)(TADDR)__sync_swap((void* volatile *)destination, value);
184 ArmInterlockedOperationBarrier();
185 return result;
186#endif
187}
188
189// Performs an atomic compare-and-exchange operation on the specified pointers.
190// Parameters:
191// destination - value to be exchanged
192// exchange - value to set the destinaton to
193// comparand - value to compare the destination to before setting it to the exchange.
194// The destination is set only if the destination is equal to the comparand.
195// Return:
196// The original value of the destination
197template <typename T>
198__forceinline T Interlocked::CompareExchangePointer(T volatile *destination, T exchange, T comparand)
199{
200#ifdef _MSC_VER
201#ifdef BIT64
202 return (T)(TADDR)_InterlockedCompareExchangePointer((void* volatile *)destination, exchange, comparand);
203#else
204 return (T)(TADDR)_InterlockedCompareExchange((long volatile *)(void* volatile *)destination, (long)(void*)exchange, (long)(void*)comparand);
205#endif
206#else
207 T result = (T)(TADDR)__sync_val_compare_and_swap((void* volatile *)destination, comparand, exchange);
208 ArmInterlockedOperationBarrier();
209 return result;
210#endif
211}
212
213template <typename T>
214__forceinline T Interlocked::CompareExchangePointer(T volatile *destination, T exchange, std::nullptr_t comparand)
215{
216#ifdef _MSC_VER
217#ifdef BIT64
218 return (T)(TADDR)_InterlockedCompareExchangePointer((void* volatile *)destination, (void*)exchange, (void*)comparand);
219#else
220 return (T)(TADDR)_InterlockedCompareExchange((long volatile *)(void* volatile *)destination, (long)(void*)exchange, (long)(void*)comparand);
221#endif
222#else
223 T result = (T)(TADDR)__sync_val_compare_and_swap((void* volatile *)destination, (void*)comparand, (void*)exchange);
224 ArmInterlockedOperationBarrier();
225 return result;
226#endif
227}
228
229#endif // __GCENV_INTERLOCKED_INL__
230