blob: 93ced8770d480eddbde2d88b7cff7278ab5ec42d [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.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * 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.
18 *
19 * 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
191inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
192 Acquire_AtomicExchange(ptr, value);
193}
194
195inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
196 *ptr = value; // works w/o barrier for current Intel chips as of June 2005
197 // See comments in Atomic64 version of Release_Store() below.
198}
199
200inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
201 return *ptr;
202}
203
204inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
205 Atomic32 value = *ptr;
206 return value;
207}
208
209inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
210 MemoryBarrier();
211 return *ptr;
212}
213
214// 64-bit operations
215
216#if defined(_WIN64) || defined(__MINGW64__)
217
218// 64-bit low-level operations on 64-bit platform.
219
220COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
221
222// These are the intrinsics needed for 64-bit operations. Similar to the
223// 32-bit case above.
224
225extern "C" {
226#if defined(__MINGW64__)
227inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
228 PVOID newval, PVOID oldval) {
229 return ::InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
230 newval, oldval);
231}
232inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
233 return ::InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
234}
235inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
236 LONGLONG increment) {
237 return ::InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
238}
239
240#elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
241// Like above, we need to declare the intrinsics ourselves.
242PVOID _InterlockedCompareExchangePointer(volatile PVOID* ptr,
243 PVOID newval, PVOID oldval);
244#pragma intrinsic(_InterlockedCompareExchangePointer)
245inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
246 PVOID newval, PVOID oldval) {
247 return _InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
248 newval, oldval);
249}
250
251PVOID _InterlockedExchangePointer(volatile PVOID* ptr, PVOID newval);
252#pragma intrinsic(_InterlockedExchangePointer)
253inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
254 return _InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
255}
256
257LONGLONG _InterlockedExchangeAdd64(volatile LONGLONG* ptr, LONGLONG increment);
258#pragma intrinsic(_InterlockedExchangeAdd64)
259inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
260 LONGLONG increment) {
261 return _InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
262}
263
264#else
265inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
266 PVOID newval, PVOID oldval) {
267 return ::InterlockedCompareExchangePointer(ptr, newval, oldval);
268}
269inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
270 return ::InterlockedExchangePointer(ptr, newval);
271}
272inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
273 LONGLONG increment) {
274 return ::InterlockedExchangeAdd64(ptr, increment);
275}
276
277#endif // ifdef __MINGW64__
278} // extern "C"
279
280inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
281 Atomic64 old_value,
282 Atomic64 new_value) {
283 PVOID result = FastInterlockedCompareExchangePointer(
284 reinterpret_cast<volatile PVOID*>(ptr),
285 reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
286 return reinterpret_cast<Atomic64>(result);
287}
288
289inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
290 Atomic64 new_value) {
291 PVOID result = FastInterlockedExchangePointer(
292 reinterpret_cast<volatile PVOID*>(ptr),
293 reinterpret_cast<PVOID>(new_value));
294 return reinterpret_cast<Atomic64>(result);
295}
296
297inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
298 *ptr = value;
299}
300
301inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
302 NoBarrier_AtomicExchange(ptr, value);
303 // acts as a barrier in this implementation
304}
305
306inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
307 *ptr = value; // works w/o barrier for current Intel chips as of June 2005
308
309 // When new chips come out, check:
310 // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
311 // System Programming Guide, Chatper 7: Multiple-processor management,
312 // Section 7.2, Memory Ordering.
313 // Last seen at:
314 // http://developer.intel.com/design/pentium4/manuals/index_new.htm
315}
316
317inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
318 return *ptr;
319}
320
321inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
322 Atomic64 value = *ptr;
323 return value;
324}
325
326inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
327 MemoryBarrier();
328 return *ptr;
329}
330
331#else // defined(_WIN64) || defined(__MINGW64__)
332
333// 64-bit low-level operations on 32-bit platform
334
335// TODO(vchen): The GNU assembly below must be converted to MSVC inline
336// assembly. Then the file should be renamed to ...-x86-msvc.h, probably.
337
338inline void NotImplementedFatalError(const char *function_name) {
339 fprintf(stderr, "64-bit %s() not implemented on this platform\n",
340 function_name);
341 abort();
342}
343
344inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
345 Atomic64 old_value,
346 Atomic64 new_value) {
347#if 0 // Not implemented
348 Atomic64 prev;
349 __asm__ __volatile__("movl (%3), %%ebx\n\t" // Move 64-bit new_value into
350 "movl 4(%3), %%ecx\n\t" // ecx:ebx
351 "lock; cmpxchg8b %1\n\t" // If edx:eax (old_value) same
352 : "=A" (prev) // as contents of ptr:
353 : "m" (*ptr), // ecx:ebx => ptr
354 "0" (old_value), // else:
355 "r" (&new_value) // old *ptr => edx:eax
356 : "memory", "%ebx", "%ecx");
357 return prev;
358#else
359 NotImplementedFatalError("NoBarrier_CompareAndSwap");
360 return 0;
361#endif
362}
363
364inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
365 Atomic64 new_value) {
366#if 0 // Not implemented
367 __asm__ __volatile__(
368 "movl (%2), %%ebx\n\t" // Move 64-bit new_value into
369 "movl 4(%2), %%ecx\n\t" // ecx:ebx
370 "0:\n\t"
371 "movl %1, %%eax\n\t" // Read contents of ptr into
372 "movl 4%1, %%edx\n\t" // edx:eax
373 "lock; cmpxchg8b %1\n\t" // Attempt cmpxchg; if *ptr
374 "jnz 0b\n\t" // is no longer edx:eax, loop
375 : "=A" (new_value)
376 : "m" (*ptr),
377 "r" (&new_value)
378 : "memory", "%ebx", "%ecx");
379 return new_value; // Now it's the previous value.
380#else
381 NotImplementedFatalError("NoBarrier_AtomicExchange");
382 return 0;
383#endif
384}
385
386inline void NoBarrier_Store(volatile Atomic64* ptrValue, Atomic64 value)
387{
388 __asm {
389 movq mm0, value; // Use mmx reg for 64-bit atomic moves
390 mov eax, ptrValue;
391 movq [eax], mm0;
392 emms; // Empty mmx state to enable FP registers
393 }
394}
395
396inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
397 NoBarrier_AtomicExchange(ptr, value);
398 // acts as a barrier in this implementation
399}
400
401inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
402 NoBarrier_Store(ptr, value);
403}
404
405inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptrValue)
406{
407 Atomic64 value;
408 __asm {
409 mov eax, ptrValue;
410 movq mm0, [eax]; // Use mmx reg for 64-bit atomic moves
411 movq value, mm0;
412 emms; // Empty mmx state to enable FP registers
413 }
414 return value;
415}
416
417inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
418 Atomic64 value = NoBarrier_Load(ptr);
419 return value;
420}
421
422inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
423 MemoryBarrier();
424 return NoBarrier_Load(ptr);
425}
426
427#endif // defined(_WIN64) || defined(__MINGW64__)
428
429
430inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
431 Atomic64 new_value) {
432 // FastInterlockedExchange has both acquire and release memory barriers.
433 return NoBarrier_AtomicExchange(ptr, new_value);
434}
435
436inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
437 Atomic64 new_value) {
438 // FastInterlockedExchange has both acquire and release memory barriers.
439 return NoBarrier_AtomicExchange(ptr, new_value);
440}
441
442inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
443 Atomic64 old_value,
444 Atomic64 new_value) {
445 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
446}
447
448inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
449 Atomic64 old_value,
450 Atomic64 new_value) {
451 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
452}
453
454} // namespace base::subtle
455} // namespace base
456
457#endif // BASE_ATOMICOPS_INTERNALS_WINDOWS_H_