blob: f7c2907e8203a0a3d91f30b0566f4059f8aebe3f [file] [log] [blame]
Austin Schuh745610d2015-09-06 18:19:50 -07001// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2/* Copyright (c) 2006, Google Inc.
3 * All rights reserved.
Brian Silverman20350ac2021-11-17 18:19:55 -08004 *
Austin Schuh745610d2015-09-06 18:19:50 -07005 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
Brian Silverman20350ac2021-11-17 18:19:55 -08008 *
Austin Schuh745610d2015-09-06 18:19:50 -07009 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
Brian Silverman20350ac2021-11-17 18:19:55 -080018 *
Austin Schuh745610d2015-09-06 18:19:50 -070019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * ---
32 * Author: Sanjay Ghemawat
33 */
34
35// Implementation of atomic operations using Windows API
36// functions. This file should not be included directly. Clients
37// should instead include "base/atomicops.h".
38
39#ifndef BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
40#define BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
41
42#include <stdio.h>
43#include <stdlib.h>
44#include "base/basictypes.h" // For COMPILE_ASSERT
45
46typedef int32 Atomic32;
47
48#if defined(_WIN64)
49#define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
50#endif
51
52namespace base {
53namespace subtle {
54
55typedef int64 Atomic64;
56
57// 32-bit low-level operations on any platform
58
59extern "C" {
60// We use windows intrinsics when we can (they seem to be supported
61// well on MSVC 8.0 and above). Unfortunately, in some
62// environments, <windows.h> and <intrin.h> have conflicting
63// declarations of some other intrinsics, breaking compilation:
64// http://connect.microsoft.com/VisualStudio/feedback/details/262047
65// Therefore, we simply declare the relevant intrinsics ourself.
66
67// MinGW has a bug in the header files where it doesn't indicate the
68// first argument is volatile -- they're not up to date. See
69// http://readlist.com/lists/lists.sourceforge.net/mingw-users/0/3861.html
70// We have to const_cast away the volatile to avoid compiler warnings.
71// TODO(csilvers): remove this once MinGW has updated MinGW/include/winbase.h
72#if defined(__MINGW32__)
73inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
74 LONG newval, LONG oldval) {
75 return ::InterlockedCompareExchange(const_cast<LONG*>(ptr), newval, oldval);
76}
77inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
78 return ::InterlockedExchange(const_cast<LONG*>(ptr), newval);
79}
80inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
81 return ::InterlockedExchangeAdd(const_cast<LONG*>(ptr), increment);
82}
83
84#elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
85// Unfortunately, in some environments, <windows.h> and <intrin.h>
86// have conflicting declarations of some intrinsics, breaking
87// compilation. So we declare the intrinsics we need ourselves. See
88// http://connect.microsoft.com/VisualStudio/feedback/details/262047
89LONG _InterlockedCompareExchange(volatile LONG* ptr, LONG newval, LONG oldval);
90#pragma intrinsic(_InterlockedCompareExchange)
91inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
92 LONG newval, LONG oldval) {
93 return _InterlockedCompareExchange(ptr, newval, oldval);
94}
95
96LONG _InterlockedExchange(volatile LONG* ptr, LONG newval);
97#pragma intrinsic(_InterlockedExchange)
98inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
99 return _InterlockedExchange(ptr, newval);
100}
101
102LONG _InterlockedExchangeAdd(volatile LONG* ptr, LONG increment);
103#pragma intrinsic(_InterlockedExchangeAdd)
104inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
105 return _InterlockedExchangeAdd(ptr, increment);
106}
107
108#else
109inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
110 LONG newval, LONG oldval) {
111 return ::InterlockedCompareExchange(ptr, newval, oldval);
112}
113inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
114 return ::InterlockedExchange(ptr, newval);
115}
116inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
117 return ::InterlockedExchangeAdd(ptr, increment);
118}
119
120#endif // ifdef __MINGW32__
121} // extern "C"
122
123inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
124 Atomic32 old_value,
125 Atomic32 new_value) {
126 LONG result = FastInterlockedCompareExchange(
127 reinterpret_cast<volatile LONG*>(ptr),
128 static_cast<LONG>(new_value),
129 static_cast<LONG>(old_value));
130 return static_cast<Atomic32>(result);
131}
132
133inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
134 Atomic32 new_value) {
135 LONG result = FastInterlockedExchange(
136 reinterpret_cast<volatile LONG*>(ptr),
137 static_cast<LONG>(new_value));
138 return static_cast<Atomic32>(result);
139}
140
141inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
142 Atomic32 new_value) {
143 // FastInterlockedExchange has both acquire and release memory barriers.
144 return NoBarrier_AtomicExchange(ptr, new_value);
145}
146
147inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
148 Atomic32 new_value) {
149 // FastInterlockedExchange has both acquire and release memory barriers.
150 return NoBarrier_AtomicExchange(ptr, new_value);
151}
152
153} // namespace base::subtle
154} // namespace base
155
156
157// In msvc8/vs2005, winnt.h already contains a definition for
158// MemoryBarrier in the global namespace. Add it there for earlier
159// versions and forward to it from within the namespace.
160#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
161inline void MemoryBarrier() {
162 Atomic32 value = 0;
163 base::subtle::NoBarrier_AtomicExchange(&value, 0);
164 // actually acts as a barrier in thisd implementation
165}
166#endif
167
168namespace base {
169namespace subtle {
170
171inline void MemoryBarrier() {
172 ::MemoryBarrier();
173}
174
175inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
176 Atomic32 old_value,
177 Atomic32 new_value) {
178 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
179}
180
181inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
182 Atomic32 old_value,
183 Atomic32 new_value) {
184 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
185}
186
187inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
188 *ptr = value;
189}
190
Austin Schuh745610d2015-09-06 18:19:50 -0700191inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
192 *ptr = value; // works w/o barrier for current Intel chips as of June 2005
193 // See comments in Atomic64 version of Release_Store() below.
194}
195
196inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
197 return *ptr;
198}
199
200inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
201 Atomic32 value = *ptr;
202 return value;
203}
204
Austin Schuh745610d2015-09-06 18:19:50 -0700205// 64-bit operations
206
207#if defined(_WIN64) || defined(__MINGW64__)
208
209// 64-bit low-level operations on 64-bit platform.
210
211COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
212
213// These are the intrinsics needed for 64-bit operations. Similar to the
214// 32-bit case above.
215
216extern "C" {
217#if defined(__MINGW64__)
218inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
219 PVOID newval, PVOID oldval) {
220 return ::InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
221 newval, oldval);
222}
223inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
224 return ::InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
225}
226inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
227 LONGLONG increment) {
228 return ::InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
229}
230
231#elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
232// Like above, we need to declare the intrinsics ourselves.
233PVOID _InterlockedCompareExchangePointer(volatile PVOID* ptr,
234 PVOID newval, PVOID oldval);
235#pragma intrinsic(_InterlockedCompareExchangePointer)
236inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
237 PVOID newval, PVOID oldval) {
238 return _InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
239 newval, oldval);
240}
241
242PVOID _InterlockedExchangePointer(volatile PVOID* ptr, PVOID newval);
243#pragma intrinsic(_InterlockedExchangePointer)
244inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
245 return _InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
246}
247
248LONGLONG _InterlockedExchangeAdd64(volatile LONGLONG* ptr, LONGLONG increment);
249#pragma intrinsic(_InterlockedExchangeAdd64)
250inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
251 LONGLONG increment) {
252 return _InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
253}
254
255#else
256inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
257 PVOID newval, PVOID oldval) {
258 return ::InterlockedCompareExchangePointer(ptr, newval, oldval);
259}
260inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
261 return ::InterlockedExchangePointer(ptr, newval);
262}
263inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
264 LONGLONG increment) {
265 return ::InterlockedExchangeAdd64(ptr, increment);
266}
267
268#endif // ifdef __MINGW64__
269} // extern "C"
270
271inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
272 Atomic64 old_value,
273 Atomic64 new_value) {
274 PVOID result = FastInterlockedCompareExchangePointer(
275 reinterpret_cast<volatile PVOID*>(ptr),
276 reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
277 return reinterpret_cast<Atomic64>(result);
278}
279
280inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
281 Atomic64 new_value) {
282 PVOID result = FastInterlockedExchangePointer(
283 reinterpret_cast<volatile PVOID*>(ptr),
284 reinterpret_cast<PVOID>(new_value));
285 return reinterpret_cast<Atomic64>(result);
286}
287
288inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
289 *ptr = value;
290}
291
Austin Schuh745610d2015-09-06 18:19:50 -0700292inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
293 *ptr = value; // works w/o barrier for current Intel chips as of June 2005
294
295 // When new chips come out, check:
296 // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
297 // System Programming Guide, Chatper 7: Multiple-processor management,
298 // Section 7.2, Memory Ordering.
299 // Last seen at:
300 // http://developer.intel.com/design/pentium4/manuals/index_new.htm
301}
302
303inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
304 return *ptr;
305}
306
307inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
308 Atomic64 value = *ptr;
309 return value;
310}
311
Austin Schuh745610d2015-09-06 18:19:50 -0700312#else // defined(_WIN64) || defined(__MINGW64__)
313
314// 64-bit low-level operations on 32-bit platform
315
316// TODO(vchen): The GNU assembly below must be converted to MSVC inline
317// assembly. Then the file should be renamed to ...-x86-msvc.h, probably.
318
319inline void NotImplementedFatalError(const char *function_name) {
320 fprintf(stderr, "64-bit %s() not implemented on this platform\n",
321 function_name);
322 abort();
323}
324
325inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
326 Atomic64 old_value,
327 Atomic64 new_value) {
328#if 0 // Not implemented
329 Atomic64 prev;
330 __asm__ __volatile__("movl (%3), %%ebx\n\t" // Move 64-bit new_value into
331 "movl 4(%3), %%ecx\n\t" // ecx:ebx
332 "lock; cmpxchg8b %1\n\t" // If edx:eax (old_value) same
333 : "=A" (prev) // as contents of ptr:
334 : "m" (*ptr), // ecx:ebx => ptr
335 "0" (old_value), // else:
336 "r" (&new_value) // old *ptr => edx:eax
337 : "memory", "%ebx", "%ecx");
338 return prev;
339#else
340 NotImplementedFatalError("NoBarrier_CompareAndSwap");
341 return 0;
342#endif
343}
344
345inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
346 Atomic64 new_value) {
347#if 0 // Not implemented
348 __asm__ __volatile__(
349 "movl (%2), %%ebx\n\t" // Move 64-bit new_value into
350 "movl 4(%2), %%ecx\n\t" // ecx:ebx
351 "0:\n\t"
352 "movl %1, %%eax\n\t" // Read contents of ptr into
353 "movl 4%1, %%edx\n\t" // edx:eax
354 "lock; cmpxchg8b %1\n\t" // Attempt cmpxchg; if *ptr
355 "jnz 0b\n\t" // is no longer edx:eax, loop
356 : "=A" (new_value)
357 : "m" (*ptr),
358 "r" (&new_value)
359 : "memory", "%ebx", "%ecx");
360 return new_value; // Now it's the previous value.
361#else
362 NotImplementedFatalError("NoBarrier_AtomicExchange");
363 return 0;
364#endif
365}
366
367inline void NoBarrier_Store(volatile Atomic64* ptrValue, Atomic64 value)
368{
369 __asm {
370 movq mm0, value; // Use mmx reg for 64-bit atomic moves
371 mov eax, ptrValue;
372 movq [eax], mm0;
373 emms; // Empty mmx state to enable FP registers
374 }
375}
376
Austin Schuh745610d2015-09-06 18:19:50 -0700377inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
378 NoBarrier_Store(ptr, value);
379}
380
381inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptrValue)
382{
383 Atomic64 value;
384 __asm {
385 mov eax, ptrValue;
386 movq mm0, [eax]; // Use mmx reg for 64-bit atomic moves
387 movq value, mm0;
388 emms; // Empty mmx state to enable FP registers
389 }
390 return value;
391}
392
393inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
394 Atomic64 value = NoBarrier_Load(ptr);
395 return value;
396}
397
Austin Schuh745610d2015-09-06 18:19:50 -0700398#endif // defined(_WIN64) || defined(__MINGW64__)
399
400
401inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
402 Atomic64 new_value) {
403 // FastInterlockedExchange has both acquire and release memory barriers.
404 return NoBarrier_AtomicExchange(ptr, new_value);
405}
406
407inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
408 Atomic64 new_value) {
409 // FastInterlockedExchange has both acquire and release memory barriers.
410 return NoBarrier_AtomicExchange(ptr, new_value);
411}
412
413inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
414 Atomic64 old_value,
415 Atomic64 new_value) {
416 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
417}
418
419inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
420 Atomic64 old_value,
421 Atomic64 new_value) {
422 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
423}
424
425} // namespace base::subtle
426} // namespace base
427
428#endif // BASE_ATOMICOPS_INTERNALS_WINDOWS_H_