1 /* Copyright (c) 2006, Google Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * Author: Sanjay Ghemawat
34 // Implementation of atomic operations using Windows API
35 // functions. This file should not be included directly. Clients
36 // should instead include "base/atomicops.h".
38 #ifndef BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
39 #define BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
43 #include "base/abort.h"
44 #include "base/basictypes.h" // For COMPILE_ASSERT
46 typedef int32 Atomic32;
49 #define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
55 typedef int64 Atomic64;
57 // 32-bit low-level operations on any platform
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.
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__)
73 inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
74 LONG newval, LONG oldval) {
75 return ::InterlockedCompareExchange(const_cast<LONG*>(ptr), newval, oldval);
77 inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
78 return ::InterlockedExchange(const_cast<LONG*>(ptr), newval);
80 inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
81 return ::InterlockedExchangeAdd(const_cast<LONG*>(ptr), increment);
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
90 // Don't declare the intrinsics if using Clang. Clang provides inline
91 // definitions in its Intrin.h.
93 LONG _InterlockedCompareExchange(volatile LONG* ptr, LONG newval, LONG oldval);
94 #pragma intrinsic(_InterlockedCompareExchange)
96 LONG _InterlockedExchange(volatile LONG* ptr, LONG newval);
97 #pragma intrinsic(_InterlockedExchange)
99 LONG _InterlockedExchangeAdd(volatile LONG* ptr, LONG increment);
100 #pragma intrinsic(_InterlockedExchangeAdd)
103 inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
104 LONG newval, LONG oldval) {
105 return _InterlockedCompareExchange(ptr, newval, oldval);
108 inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
109 return _InterlockedExchange(ptr, newval);
112 inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
113 return _InterlockedExchangeAdd(ptr, increment);
117 inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
118 LONG newval, LONG oldval) {
119 return ::InterlockedCompareExchange(ptr, newval, oldval);
121 inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
122 return ::InterlockedExchange(ptr, newval);
124 inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
125 return ::InterlockedExchangeAdd(ptr, increment);
128 #endif // ifdef __MINGW32__
131 inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
133 Atomic32 new_value) {
134 LONG result = FastInterlockedCompareExchange(
135 reinterpret_cast<volatile LONG*>(ptr),
136 static_cast<LONG>(new_value),
137 static_cast<LONG>(old_value));
138 return static_cast<Atomic32>(result);
141 inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
142 Atomic32 new_value) {
143 LONG result = FastInterlockedExchange(
144 reinterpret_cast<volatile LONG*>(ptr),
145 static_cast<LONG>(new_value));
146 return static_cast<Atomic32>(result);
149 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
150 Atomic32 increment) {
151 return FastInterlockedExchangeAdd(
152 reinterpret_cast<volatile LONG*>(ptr),
153 static_cast<LONG>(increment)) + increment;
156 inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
157 Atomic32 increment) {
158 return Barrier_AtomicIncrement(ptr, increment);
161 } // namespace base::subtle
165 // In msvc8/vs2005, winnt.h already contains a definition for
166 // MemoryBarrier in the global namespace. Add it there for earlier
167 // versions and forward to it from within the namespace.
168 #if !(defined(_MSC_VER) && _MSC_VER >= 1400)
169 inline void MemoryBarrier() {
171 base::subtle::NoBarrier_AtomicExchange(&value, 0);
172 // actually acts as a barrier in thisd implementation
179 inline void MemoryBarrier() {
183 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
185 Atomic32 new_value) {
186 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
189 inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
191 Atomic32 new_value) {
192 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
195 inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
199 inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
200 NoBarrier_AtomicExchange(ptr, value);
201 // acts as a barrier in this implementation
204 inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
205 *ptr = value; // works w/o barrier for current Intel chips as of June 2005
206 // See comments in Atomic64 version of Release_Store() below.
209 inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
213 inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
214 Atomic32 value = *ptr;
218 inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
225 #if defined(_WIN64) || defined(__MINGW64__)
227 // 64-bit low-level operations on 64-bit platform.
229 COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
231 // These are the intrinsics needed for 64-bit operations. Similar to the
232 // 32-bit case above.
235 #if defined(__MINGW64__)
236 inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
237 PVOID newval, PVOID oldval) {
238 return ::InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
241 inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
242 return ::InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
244 inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
245 LONGLONG increment) {
246 return ::InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
249 #elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
250 // Like above, we need to declare the intrinsics ourselves.
251 PVOID _InterlockedCompareExchangePointer(volatile PVOID* ptr,
252 PVOID newval, PVOID oldval);
253 #pragma intrinsic(_InterlockedCompareExchangePointer)
254 inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
255 PVOID newval, PVOID oldval) {
256 return _InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
260 PVOID _InterlockedExchangePointer(volatile PVOID* ptr, PVOID newval);
261 #pragma intrinsic(_InterlockedExchangePointer)
262 inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
263 return _InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
266 LONGLONG _InterlockedExchangeAdd64(volatile LONGLONG* ptr, LONGLONG increment);
267 #pragma intrinsic(_InterlockedExchangeAdd64)
268 inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
269 LONGLONG increment) {
270 return _InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
274 inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
275 PVOID newval, PVOID oldval) {
276 return ::InterlockedCompareExchangePointer(ptr, newval, oldval);
278 inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
279 return ::InterlockedExchangePointer(ptr, newval);
281 inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
282 LONGLONG increment) {
283 return ::InterlockedExchangeAdd64(ptr, increment);
286 #endif // ifdef __MINGW64__
289 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
291 Atomic64 new_value) {
292 PVOID result = FastInterlockedCompareExchangePointer(
293 reinterpret_cast<volatile PVOID*>(ptr),
294 reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
295 return reinterpret_cast<Atomic64>(result);
298 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
299 Atomic64 new_value) {
300 PVOID result = FastInterlockedExchangePointer(
301 reinterpret_cast<volatile PVOID*>(ptr),
302 reinterpret_cast<PVOID>(new_value));
303 return reinterpret_cast<Atomic64>(result);
306 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
307 Atomic64 increment) {
308 return FastInterlockedExchangeAdd64(
309 reinterpret_cast<volatile LONGLONG*>(ptr),
310 static_cast<LONGLONG>(increment)) + increment;
313 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
314 Atomic64 increment) {
315 return Barrier_AtomicIncrement(ptr, increment);
318 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
322 inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
323 NoBarrier_AtomicExchange(ptr, value);
324 // acts as a barrier in this implementation
327 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
328 *ptr = value; // works w/o barrier for current Intel chips as of June 2005
330 // When new chips come out, check:
331 // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
332 // System Programming Guide, Chatper 7: Multiple-processor management,
333 // Section 7.2, Memory Ordering.
335 // http://developer.intel.com/design/pentium4/manuals/index_new.htm
338 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
342 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
343 Atomic64 value = *ptr;
347 inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
352 #else // defined(_WIN64) || defined(__MINGW64__)
354 // 64-bit low-level operations on 32-bit platform
356 // TODO(vchen): The GNU assembly below must be converted to MSVC inline
357 // assembly. Then the file should be renamed to ...-x86-msvc.h, probably.
359 inline void NotImplementedFatalError(const char *function_name) {
360 fprintf(stderr, "64-bit %s() not implemented on this platform\n",
365 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
367 Atomic64 new_value) {
368 #if 0 // Not implemented
370 __asm__ __volatile__("movl (%3), %%ebx\n\t" // Move 64-bit new_value into
371 "movl 4(%3), %%ecx\n\t" // ecx:ebx
372 "lock; cmpxchg8b %1\n\t" // If edx:eax (old_value) same
373 : "=A" (prev) // as contents of ptr:
374 : "m" (*ptr), // ecx:ebx => ptr
375 "0" (old_value), // else:
376 "r" (&new_value) // old *ptr => edx:eax
377 : "memory", "%ebx", "%ecx");
380 NotImplementedFatalError("NoBarrier_CompareAndSwap");
385 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
386 Atomic64 new_value) {
387 #if 0 // Not implemented
388 __asm__ __volatile__(
389 "movl (%2), %%ebx\n\t" // Move 64-bit new_value into
390 "movl 4(%2), %%ecx\n\t" // ecx:ebx
392 "movl %1, %%eax\n\t" // Read contents of ptr into
393 "movl 4%1, %%edx\n\t" // edx:eax
394 "lock; cmpxchg8b %1\n\t" // Attempt cmpxchg; if *ptr
395 "jnz 0b\n\t" // is no longer edx:eax, loop
399 : "memory", "%ebx", "%ecx");
400 return new_value; // Now it's the previous value.
402 NotImplementedFatalError("NoBarrier_AtomicExchange");
407 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
408 Atomic64 increment) {
409 #if 0 // Not implemented
410 Atomic64 temp = increment;
411 __asm__ __volatile__(
413 "movl (%3), %%ebx\n\t" // Move 64-bit increment into
414 "movl 4(%3), %%ecx\n\t" // ecx:ebx
415 "movl (%2), %%eax\n\t" // Read contents of ptr into
416 "movl 4(%2), %%edx\n\t" // edx:eax
417 "add %%eax, %%ebx\n\t" // sum => ecx:ebx
418 "adc %%edx, %%ecx\n\t" // edx:eax still has old *ptr
419 "lock; cmpxchg8b (%2)\n\t"// Attempt cmpxchg; if *ptr
420 "jnz 0b\n\t" // is no longer edx:eax, loop
421 : "=A"(temp), "+m"(*ptr)
422 : "D" (ptr), "S" (&increment)
423 : "memory", "%ebx", "%ecx");
424 // temp now contains the previous value of *ptr
425 return temp + increment;
427 NotImplementedFatalError("NoBarrier_AtomicIncrement");
432 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
433 Atomic64 increment) {
434 #if 0 // Not implemented
435 Atomic64 new_val = NoBarrier_AtomicIncrement(ptr, increment);
436 if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
437 __asm__ __volatile__("lfence" : : : "memory");
441 NotImplementedFatalError("Barrier_AtomicIncrement");
446 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
447 #if 0 // Not implemented
449 mov mm0, value; // Use mmx reg for 64-bit atomic moves
451 emms; // Empty mmx state to enable FP registers
454 NotImplementedFatalError("NoBarrier_Store");
458 inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
459 NoBarrier_AtomicExchange(ptr, value);
460 // acts as a barrier in this implementation
463 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
464 NoBarrier_Store(ptr, value);
467 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
468 #if 0 // Not implemented
471 mov mm0, ptr; // Use mmx reg for 64-bit atomic moves
473 emms; // Empty mmx state to enable FP registers
477 NotImplementedFatalError("NoBarrier_Store");
482 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
483 Atomic64 value = NoBarrier_Load(ptr);
487 inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
489 return NoBarrier_Load(ptr);
492 #endif // defined(_WIN64) || defined(__MINGW64__)
495 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
497 Atomic64 new_value) {
498 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
501 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
503 Atomic64 new_value) {
504 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
507 } // namespace base::subtle
510 #endif // BASE_ATOMICOPS_INTERNALS_WINDOWS_H_