atomicops-internals-windows.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // -*- 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. // Implementation of atomic operations using Windows API
  35. // functions. This file should not be included directly. Clients
  36. // should instead include "base/atomicops.h".
  37. #ifndef BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
  38. #define BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include "base/basictypes.h" // For COMPILE_ASSERT
  42. typedef int32 Atomic32;
  43. #if defined(_WIN64)
  44. #define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
  45. #endif
  46. namespace base {
  47. namespace subtle {
  48. typedef int64 Atomic64;
  49. // 32-bit low-level operations on any platform
  50. extern "C" {
  51. // We use windows intrinsics when we can (they seem to be supported
  52. // well on MSVC 8.0 and above). Unfortunately, in some
  53. // environments, <windows.h> and <intrin.h> have conflicting
  54. // declarations of some other intrinsics, breaking compilation:
  55. // http://connect.microsoft.com/VisualStudio/feedback/details/262047
  56. // Therefore, we simply declare the relevant intrinsics ourself.
  57. // MinGW has a bug in the header files where it doesn't indicate the
  58. // first argument is volatile -- they're not up to date. See
  59. // http://readlist.com/lists/lists.sourceforge.net/mingw-users/0/3861.html
  60. // We have to const_cast away the volatile to avoid compiler warnings.
  61. // TODO(csilvers): remove this once MinGW has updated MinGW/include/winbase.h
  62. #if defined(__MINGW32__)
  63. inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
  64. LONG newval, LONG oldval) {
  65. return ::InterlockedCompareExchange(const_cast<LONG*>(ptr), newval, oldval);
  66. }
  67. inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
  68. return ::InterlockedExchange(const_cast<LONG*>(ptr), newval);
  69. }
  70. inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
  71. return ::InterlockedExchangeAdd(const_cast<LONG*>(ptr), increment);
  72. }
  73. #elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
  74. // Unfortunately, in some environments, <windows.h> and <intrin.h>
  75. // have conflicting declarations of some intrinsics, breaking
  76. // compilation. So we declare the intrinsics we need ourselves. See
  77. // http://connect.microsoft.com/VisualStudio/feedback/details/262047
  78. LONG _InterlockedCompareExchange(volatile LONG* ptr, LONG newval, LONG oldval);
  79. #pragma intrinsic(_InterlockedCompareExchange)
  80. inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
  81. LONG newval, LONG oldval) {
  82. return _InterlockedCompareExchange(ptr, newval, oldval);
  83. }
  84. LONG _InterlockedExchange(volatile LONG* ptr, LONG newval);
  85. #pragma intrinsic(_InterlockedExchange)
  86. inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
  87. return _InterlockedExchange(ptr, newval);
  88. }
  89. LONG _InterlockedExchangeAdd(volatile LONG* ptr, LONG increment);
  90. #pragma intrinsic(_InterlockedExchangeAdd)
  91. inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
  92. return _InterlockedExchangeAdd(ptr, increment);
  93. }
  94. #else
  95. inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
  96. LONG newval, LONG oldval) {
  97. return ::InterlockedCompareExchange(ptr, newval, oldval);
  98. }
  99. inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
  100. return ::InterlockedExchange(ptr, newval);
  101. }
  102. inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
  103. return ::InterlockedExchangeAdd(ptr, increment);
  104. }
  105. #endif // ifdef __MINGW32__
  106. } // extern "C"
  107. inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
  108. Atomic32 old_value,
  109. Atomic32 new_value) {
  110. LONG result = FastInterlockedCompareExchange(
  111. reinterpret_cast<volatile LONG*>(ptr),
  112. static_cast<LONG>(new_value),
  113. static_cast<LONG>(old_value));
  114. return static_cast<Atomic32>(result);
  115. }
  116. inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
  117. Atomic32 new_value) {
  118. LONG result = FastInterlockedExchange(
  119. reinterpret_cast<volatile LONG*>(ptr),
  120. static_cast<LONG>(new_value));
  121. return static_cast<Atomic32>(result);
  122. }
  123. inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
  124. Atomic32 new_value) {
  125. // FastInterlockedExchange has both acquire and release memory barriers.
  126. return NoBarrier_AtomicExchange(ptr, new_value);
  127. }
  128. inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
  129. Atomic32 new_value) {
  130. // FastInterlockedExchange has both acquire and release memory barriers.
  131. return NoBarrier_AtomicExchange(ptr, new_value);
  132. }
  133. } // namespace base::subtle
  134. } // namespace base
  135. // In msvc8/vs2005, winnt.h already contains a definition for
  136. // MemoryBarrier in the global namespace. Add it there for earlier
  137. // versions and forward to it from within the namespace.
  138. #if !(defined(_MSC_VER) && _MSC_VER >= 1400)
  139. inline void MemoryBarrier() {
  140. Atomic32 value = 0;
  141. base::subtle::NoBarrier_AtomicExchange(&value, 0);
  142. // actually acts as a barrier in thisd implementation
  143. }
  144. #endif
  145. namespace base {
  146. namespace subtle {
  147. inline void MemoryBarrier() {
  148. ::MemoryBarrier();
  149. }
  150. inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
  151. Atomic32 old_value,
  152. Atomic32 new_value) {
  153. return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
  154. }
  155. inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
  156. Atomic32 old_value,
  157. Atomic32 new_value) {
  158. return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
  159. }
  160. inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
  161. *ptr = value;
  162. }
  163. inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
  164. Acquire_AtomicExchange(ptr, value);
  165. }
  166. inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
  167. *ptr = value; // works w/o barrier for current Intel chips as of June 2005
  168. // See comments in Atomic64 version of Release_Store() below.
  169. }
  170. inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
  171. return *ptr;
  172. }
  173. inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
  174. Atomic32 value = *ptr;
  175. return value;
  176. }
  177. inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
  178. MemoryBarrier();
  179. return *ptr;
  180. }
  181. // 64-bit operations
  182. #if defined(_WIN64) || defined(__MINGW64__)
  183. // 64-bit low-level operations on 64-bit platform.
  184. COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
  185. // These are the intrinsics needed for 64-bit operations. Similar to the
  186. // 32-bit case above.
  187. extern "C" {
  188. #if defined(__MINGW64__)
  189. inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
  190. PVOID newval, PVOID oldval) {
  191. return ::InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
  192. newval, oldval);
  193. }
  194. inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
  195. return ::InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
  196. }
  197. inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
  198. LONGLONG increment) {
  199. return ::InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
  200. }
  201. #elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
  202. // Like above, we need to declare the intrinsics ourselves.
  203. PVOID _InterlockedCompareExchangePointer(volatile PVOID* ptr,
  204. PVOID newval, PVOID oldval);
  205. #pragma intrinsic(_InterlockedCompareExchangePointer)
  206. inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
  207. PVOID newval, PVOID oldval) {
  208. return _InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
  209. newval, oldval);
  210. }
  211. PVOID _InterlockedExchangePointer(volatile PVOID* ptr, PVOID newval);
  212. #pragma intrinsic(_InterlockedExchangePointer)
  213. inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
  214. return _InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
  215. }
  216. LONGLONG _InterlockedExchangeAdd64(volatile LONGLONG* ptr, LONGLONG increment);
  217. #pragma intrinsic(_InterlockedExchangeAdd64)
  218. inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
  219. LONGLONG increment) {
  220. return _InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
  221. }
  222. #else
  223. inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
  224. PVOID newval, PVOID oldval) {
  225. return ::InterlockedCompareExchangePointer(ptr, newval, oldval);
  226. }
  227. inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
  228. return ::InterlockedExchangePointer(ptr, newval);
  229. }
  230. inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
  231. LONGLONG increment) {
  232. return ::InterlockedExchangeAdd64(ptr, increment);
  233. }
  234. #endif // ifdef __MINGW64__
  235. } // extern "C"
  236. inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
  237. Atomic64 old_value,
  238. Atomic64 new_value) {
  239. PVOID result = FastInterlockedCompareExchangePointer(
  240. reinterpret_cast<volatile PVOID*>(ptr),
  241. reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
  242. return reinterpret_cast<Atomic64>(result);
  243. }
  244. inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
  245. Atomic64 new_value) {
  246. PVOID result = FastInterlockedExchangePointer(
  247. reinterpret_cast<volatile PVOID*>(ptr),
  248. reinterpret_cast<PVOID>(new_value));
  249. return reinterpret_cast<Atomic64>(result);
  250. }
  251. inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
  252. *ptr = value;
  253. }
  254. inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
  255. NoBarrier_AtomicExchange(ptr, value);
  256. // acts as a barrier in this implementation
  257. }
  258. inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
  259. *ptr = value; // works w/o barrier for current Intel chips as of June 2005
  260. // When new chips come out, check:
  261. // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
  262. // System Programming Guide, Chatper 7: Multiple-processor management,
  263. // Section 7.2, Memory Ordering.
  264. // Last seen at:
  265. // http://developer.intel.com/design/pentium4/manuals/index_new.htm
  266. }
  267. inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
  268. return *ptr;
  269. }
  270. inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
  271. Atomic64 value = *ptr;
  272. return value;
  273. }
  274. inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
  275. MemoryBarrier();
  276. return *ptr;
  277. }
  278. #else // defined(_WIN64) || defined(__MINGW64__)
  279. // 64-bit low-level operations on 32-bit platform
  280. // TODO(vchen): The GNU assembly below must be converted to MSVC inline
  281. // assembly. Then the file should be renamed to ...-x86-msvc.h, probably.
  282. inline void NotImplementedFatalError(const char *function_name) {
  283. fprintf(stderr, "64-bit %s() not implemented on this platform\n",
  284. function_name);
  285. abort();
  286. }
  287. inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
  288. Atomic64 old_value,
  289. Atomic64 new_value) {
  290. #if 0 // Not implemented
  291. Atomic64 prev;
  292. __asm__ __volatile__("movl (%3), %%ebx\n\t" // Move 64-bit new_value into
  293. "movl 4(%3), %%ecx\n\t" // ecx:ebx
  294. "lock; cmpxchg8b %1\n\t" // If edx:eax (old_value) same
  295. : "=A" (prev) // as contents of ptr:
  296. : "m" (*ptr), // ecx:ebx => ptr
  297. "0" (old_value), // else:
  298. "r" (&new_value) // old *ptr => edx:eax
  299. : "memory", "%ebx", "%ecx");
  300. return prev;
  301. #else
  302. NotImplementedFatalError("NoBarrier_CompareAndSwap");
  303. return 0;
  304. #endif
  305. }
  306. inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
  307. Atomic64 new_value) {
  308. #if 0 // Not implemented
  309. __asm__ __volatile__(
  310. "movl (%2), %%ebx\n\t" // Move 64-bit new_value into
  311. "movl 4(%2), %%ecx\n\t" // ecx:ebx
  312. "0:\n\t"
  313. "movl %1, %%eax\n\t" // Read contents of ptr into
  314. "movl 4%1, %%edx\n\t" // edx:eax
  315. "lock; cmpxchg8b %1\n\t" // Attempt cmpxchg; if *ptr
  316. "jnz 0b\n\t" // is no longer edx:eax, loop
  317. : "=A" (new_value)
  318. : "m" (*ptr),
  319. "r" (&new_value)
  320. : "memory", "%ebx", "%ecx");
  321. return new_value; // Now it's the previous value.
  322. #else
  323. NotImplementedFatalError("NoBarrier_AtomicExchange");
  324. return 0;
  325. #endif
  326. }
  327. inline void NoBarrier_Store(volatile Atomic64* ptrValue, Atomic64 value)
  328. {
  329. __asm {
  330. movq mm0, value; // Use mmx reg for 64-bit atomic moves
  331. mov eax, ptrValue;
  332. movq [eax], mm0;
  333. emms; // Empty mmx state to enable FP registers
  334. }
  335. }
  336. inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
  337. NoBarrier_AtomicExchange(ptr, value);
  338. // acts as a barrier in this implementation
  339. }
  340. inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
  341. NoBarrier_Store(ptr, value);
  342. }
  343. inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptrValue)
  344. {
  345. Atomic64 value;
  346. __asm {
  347. mov eax, ptrValue;
  348. movq mm0, [eax]; // Use mmx reg for 64-bit atomic moves
  349. movq value, mm0;
  350. emms; // Empty mmx state to enable FP registers
  351. }
  352. return value;
  353. }
  354. inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
  355. Atomic64 value = NoBarrier_Load(ptr);
  356. return value;
  357. }
  358. inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
  359. MemoryBarrier();
  360. return NoBarrier_Load(ptr);
  361. }
  362. #endif // defined(_WIN64) || defined(__MINGW64__)
  363. inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
  364. Atomic64 new_value) {
  365. // FastInterlockedExchange has both acquire and release memory barriers.
  366. return NoBarrier_AtomicExchange(ptr, new_value);
  367. }
  368. inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
  369. Atomic64 new_value) {
  370. // FastInterlockedExchange has both acquire and release memory barriers.
  371. return NoBarrier_AtomicExchange(ptr, new_value);
  372. }
  373. inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
  374. Atomic64 old_value,
  375. Atomic64 new_value) {
  376. return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
  377. }
  378. inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
  379. Atomic64 old_value,
  380. Atomic64 new_value) {
  381. return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
  382. }
  383. } // namespace base::subtle
  384. } // namespace base
  385. #endif // BASE_ATOMICOPS_INTERNALS_WINDOWS_H_