/root/doris/be/src/gutil/atomicops-internals-x86.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2003 Google Inc. |
2 | | // |
3 | | // Licensed to the Apache Software Foundation (ASF) under one |
4 | | // or more contributor license agreements. See the NOTICE file |
5 | | // distributed with this work for additional information |
6 | | // regarding copyright ownership. The ASF licenses this file |
7 | | // to you under the Apache License, Version 2.0 (the |
8 | | // "License"); you may not use this file except in compliance |
9 | | // with the License. You may obtain a copy of the License at |
10 | | // |
11 | | // http://www.apache.org/licenses/LICENSE-2.0 |
12 | | // |
13 | | // Unless required by applicable law or agreed to in writing, |
14 | | // software distributed under the License is distributed on an |
15 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
16 | | // KIND, either express or implied. See the License for the |
17 | | // specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | // All Rights Reserved. |
21 | | // |
22 | | // |
23 | | // Implementation of atomic operations for x86. This file should not |
24 | | // be included directly. Clients should instead include |
25 | | // "base/atomicops.h". |
26 | | |
27 | | #pragma once |
28 | | |
29 | | #include "common/logging.h" |
30 | | #include <stdint.h> |
31 | | #include <ostream> |
32 | | |
33 | | #define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic* |
34 | | |
35 | | // NOTE(user): x86 does not need to define AtomicWordCastType, because it |
36 | | // already matches Atomic32 or Atomic64, depending on the platform. |
37 | | |
38 | | // This struct is not part of the public API of this module; clients may not |
39 | | // use it. |
40 | | // Features of this x86. Values may not be correct before InitGoogle() is run, |
41 | | // but are set conservatively. |
42 | | // Modify AtomicOps_x86CPUFeatureStruct to GutilAtomicOps_x86CPUFeatureStruct for brpc |
43 | | struct GutilAtomicOps_x86CPUFeatureStruct { |
44 | | bool has_sse2; // Processor has SSE2. |
45 | | bool has_cmpxchg16b; // Processor supports cmpxchg16b instruction. |
46 | | }; |
47 | | extern struct GutilAtomicOps_x86CPUFeatureStruct GutilAtomicOps_Internalx86CPUFeatures; |
48 | | |
49 | 9.37k | #define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory") |
50 | | |
51 | | // AtomicOps initialisation for open source use. |
52 | | void AtomicOps_x86CPUFeaturesInit(); |
53 | | |
54 | | typedef int32_t Atomic32; |
55 | | typedef int64_t Atomic64; |
56 | | |
57 | | namespace base { |
58 | | namespace subtle { |
59 | | |
60 | | typedef int32_t Atomic32; |
61 | | typedef int64_t Atomic64; |
62 | | |
63 | | // These atomic primitives don't work atomically, and can cause really nasty |
64 | | // hard-to-track-down bugs, if the pointer isn't naturally aligned. Check alignment |
65 | | // in debug mode. |
66 | | template <class T> |
67 | 21.1k | void CheckNaturalAlignment(const T* ptr) { |
68 | 21.1k | DCHECK_EQ(0, reinterpret_cast<const uintptr_t>(ptr) & (sizeof(T) - 1)) |
69 | 0 | << "unaligned pointer not allowed for atomics"; |
70 | 21.1k | } _ZN4base6subtle21CheckNaturalAlignmentIViEEvPKT_ Line | Count | Source | 67 | 17.6k | void CheckNaturalAlignment(const T* ptr) { | 68 | 17.6k | DCHECK_EQ(0, reinterpret_cast<const uintptr_t>(ptr) & (sizeof(T) - 1)) | 69 | 0 | << "unaligned pointer not allowed for atomics"; | 70 | 17.6k | } |
_ZN4base6subtle21CheckNaturalAlignmentIVlEEvPKT_ Line | Count | Source | 67 | 3.48k | void CheckNaturalAlignment(const T* ptr) { | 68 | 3.48k | DCHECK_EQ(0, reinterpret_cast<const uintptr_t>(ptr) & (sizeof(T) - 1)) | 69 | 0 | << "unaligned pointer not allowed for atomics"; | 70 | 3.48k | } |
|
71 | | |
72 | | // 32-bit low-level operations on any platform. |
73 | | |
74 | | inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, |
75 | 0 | Atomic32 new_value) { |
76 | 0 | CheckNaturalAlignment(ptr); |
77 | 0 | Atomic32 prev; |
78 | 0 | __asm__ __volatile__("lock; cmpxchgl %1,%2" |
79 | 0 | : "=a"(prev) |
80 | 0 | : "q"(new_value), "m"(*ptr), "0"(old_value) |
81 | 0 | : "memory"); |
82 | 0 | return prev; |
83 | 0 | } |
84 | | |
85 | 0 | inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { |
86 | 0 | CheckNaturalAlignment(ptr); |
87 | 0 | __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg. |
88 | 0 | : "=r"(new_value) |
89 | 0 | : "m"(*ptr), "0"(new_value) |
90 | 0 | : "memory"); |
91 | 0 | return new_value; // Now it's the previous value. |
92 | 0 | } |
93 | | |
94 | 0 | inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { |
95 | 0 | CheckNaturalAlignment(ptr); |
96 | 0 | Atomic32 old_val = NoBarrier_AtomicExchange(ptr, new_value); |
97 | 0 | return old_val; |
98 | 0 | } |
99 | | |
100 | 0 | inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { |
101 | 0 | return NoBarrier_AtomicExchange(ptr, new_value); |
102 | 0 | } |
103 | | |
104 | 5.90k | inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { |
105 | 5.90k | CheckNaturalAlignment(ptr); |
106 | 5.90k | Atomic32 temp = increment; |
107 | 5.90k | __asm__ __volatile__("lock; xaddl %0,%1" : "+r"(temp), "+m"(*ptr) : : "memory"); |
108 | | // temp now holds the old value of *ptr |
109 | 5.90k | return temp + increment; |
110 | 5.90k | } |
111 | | |
112 | 5.89k | inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { |
113 | 5.89k | CheckNaturalAlignment(ptr); |
114 | 5.89k | Atomic32 temp = increment; |
115 | 5.89k | __asm__ __volatile__("lock; xaddl %0,%1" : "+r"(temp), "+m"(*ptr) : : "memory"); |
116 | | // temp now holds the old value of *ptr |
117 | 5.89k | return temp + increment; |
118 | 5.89k | } |
119 | | |
120 | | // On x86, the NoBarrier_CompareAndSwap() uses a locked instruction and so also |
121 | | // provides both acquire and release barriers. |
122 | | inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, |
123 | 0 | Atomic32 new_value) { |
124 | 0 | return NoBarrier_CompareAndSwap(ptr, old_value, new_value); |
125 | 0 | } |
126 | | |
127 | | inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, |
128 | 0 | Atomic32 new_value) { |
129 | 0 | return NoBarrier_CompareAndSwap(ptr, old_value, new_value); |
130 | 0 | } |
131 | | |
132 | | inline Atomic32 Barrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, |
133 | 0 | Atomic32 new_value) { |
134 | 0 | return NoBarrier_CompareAndSwap(ptr, old_value, new_value); |
135 | 0 | } |
136 | | |
137 | 0 | inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { |
138 | 0 | CheckNaturalAlignment(ptr); |
139 | 0 | *ptr = value; |
140 | 0 | } |
141 | | |
142 | | // Issue the x86 "pause" instruction, which tells the CPU that we |
143 | | // are in a spinlock wait loop and should allow other hyperthreads |
144 | | // to run, not speculate memory access, etc. |
145 | 0 | inline void PauseCPU() { |
146 | 0 | __asm__ __volatile__("pause" : : : "memory"); |
147 | 0 | } |
148 | | |
149 | | #if defined(__x86_64__) |
150 | | |
151 | | // 64-bit implementations of memory barrier can be simpler, because it |
152 | | // "mfence" is guaranteed to exist. |
153 | 0 | inline void MemoryBarrier() { |
154 | 0 | __asm__ __volatile__("mfence" : : : "memory"); |
155 | 0 | } |
156 | | |
157 | 0 | inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { |
158 | 0 | CheckNaturalAlignment(ptr); |
159 | 0 | *ptr = value; |
160 | 0 | MemoryBarrier(); |
161 | 0 | } |
162 | | |
163 | | #else |
164 | | |
165 | | inline void MemoryBarrier() { |
166 | | if (GutilAtomicOps_Internalx86CPUFeatures.has_sse2) { |
167 | | __asm__ __volatile__("mfence" : : : "memory"); |
168 | | } else { // mfence is faster but not present on PIII |
169 | | Atomic32 x = 0; |
170 | | Acquire_AtomicExchange(&x, 0); |
171 | | } |
172 | | } |
173 | | |
174 | | inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { |
175 | | if (GutilAtomicOps_Internalx86CPUFeatures.has_sse2) { |
176 | | CheckNaturalAlignment(ptr); |
177 | | *ptr = value; |
178 | | __asm__ __volatile__("mfence" : : : "memory"); |
179 | | } else { |
180 | | Acquire_AtomicExchange(ptr, value); |
181 | | } |
182 | | } |
183 | | #endif |
184 | | |
185 | 0 | inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { |
186 | 0 | CheckNaturalAlignment(ptr); |
187 | 0 | ATOMICOPS_COMPILER_BARRIER(); |
188 | 0 | *ptr = value; // An x86 store acts as a release barrier. |
189 | 0 | // See comments in Atomic64 version of Release_Store(), below. |
190 | 0 | } |
191 | | |
192 | 0 | inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { |
193 | 0 | CheckNaturalAlignment(ptr); |
194 | 0 | return *ptr; |
195 | 0 | } |
196 | | |
197 | 5.89k | inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { |
198 | 5.89k | CheckNaturalAlignment(ptr); |
199 | 5.89k | Atomic32 value = *ptr; // An x86 load acts as a acquire barrier. |
200 | | // See comments in Atomic64 version of Release_Store(), below. |
201 | 5.89k | ATOMICOPS_COMPILER_BARRIER(); |
202 | 5.89k | return value; |
203 | 5.89k | } |
204 | | |
205 | 0 | inline Atomic32 Release_Load(volatile const Atomic32* ptr) { |
206 | 0 | CheckNaturalAlignment(ptr); |
207 | 0 | MemoryBarrier(); |
208 | 0 | return *ptr; |
209 | 0 | } |
210 | | |
211 | | #if defined(__x86_64__) |
212 | | |
213 | | // 64-bit low-level operations on 64-bit platform. |
214 | | |
215 | | inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, |
216 | 0 | Atomic64 new_value) { |
217 | 0 | Atomic64 prev; |
218 | 0 | CheckNaturalAlignment(ptr); |
219 | 0 | __asm__ __volatile__("lock; cmpxchgq %1,%2" |
220 | 0 | : "=a"(prev) |
221 | 0 | : "q"(new_value), "m"(*ptr), "0"(old_value) |
222 | 0 | : "memory"); |
223 | 0 | return prev; |
224 | 0 | } |
225 | | |
226 | 0 | inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value) { |
227 | 0 | CheckNaturalAlignment(ptr); |
228 | 0 | __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg. |
229 | 0 | : "=r"(new_value) |
230 | 0 | : "m"(*ptr), "0"(new_value) |
231 | 0 | : "memory"); |
232 | 0 | return new_value; // Now it's the previous value. |
233 | 0 | } |
234 | | |
235 | 0 | inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value) { |
236 | 0 | Atomic64 old_val = NoBarrier_AtomicExchange(ptr, new_value); |
237 | 0 | return old_val; |
238 | 0 | } |
239 | | |
240 | 0 | inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value) { |
241 | 0 | return NoBarrier_AtomicExchange(ptr, new_value); |
242 | 0 | } |
243 | | |
244 | 0 | inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { |
245 | 0 | Atomic64 temp = increment; |
246 | 0 | CheckNaturalAlignment(ptr); |
247 | 0 | __asm__ __volatile__("lock; xaddq %0,%1" : "+r"(temp), "+m"(*ptr) : : "memory"); |
248 | | // temp now contains the previous value of *ptr |
249 | 0 | return temp + increment; |
250 | 0 | } |
251 | | |
252 | 0 | inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { |
253 | 0 | Atomic64 temp = increment; |
254 | 0 | CheckNaturalAlignment(ptr); |
255 | 0 | __asm__ __volatile__("lock; xaddq %0,%1" : "+r"(temp), "+m"(*ptr) : : "memory"); |
256 | | // temp now contains the previous value of *ptr |
257 | 0 | return temp + increment; |
258 | 0 | } |
259 | | |
260 | 0 | inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { |
261 | 0 | CheckNaturalAlignment(ptr); |
262 | 0 | *ptr = value; |
263 | 0 | } |
264 | | |
265 | 0 | inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { |
266 | 0 | CheckNaturalAlignment(ptr); |
267 | 0 | *ptr = value; |
268 | 0 | MemoryBarrier(); |
269 | 0 | } |
270 | | |
271 | 2.43k | inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { |
272 | 2.43k | ATOMICOPS_COMPILER_BARRIER(); |
273 | 2.43k | CheckNaturalAlignment(ptr); |
274 | 2.43k | *ptr = value; // An x86 store acts as a release barrier |
275 | | // for current AMD/Intel chips as of Jan 2008. |
276 | | // See also Acquire_Load(), below. |
277 | | |
278 | | // When new chips come out, check: |
279 | | // IA-32 Intel Architecture Software Developer's Manual, Volume 3: |
280 | | // System Programming Guide, Chatper 7: Multiple-processor management, |
281 | | // Section 7.2, Memory Ordering. |
282 | | // Last seen at: |
283 | | // http://developer.intel.com/design/pentium4/manuals/index_new.htm |
284 | | // |
285 | | // x86 stores/loads fail to act as barriers for a few instructions (clflush |
286 | | // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are |
287 | | // not generated by the compiler, and are rare. Users of these instructions |
288 | | // need to know about cache behaviour in any case since all of these involve |
289 | | // either flushing cache lines or non-temporal cache hints. |
290 | 2.43k | } |
291 | | |
292 | 0 | inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { |
293 | 0 | CheckNaturalAlignment(ptr); |
294 | 0 | return *ptr; |
295 | 0 | } |
296 | | |
297 | 1.04k | inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { |
298 | 1.04k | CheckNaturalAlignment(ptr); |
299 | 1.04k | Atomic64 value = *ptr; // An x86 load acts as a acquire barrier, |
300 | | // for current AMD/Intel chips as of Jan 2008. |
301 | | // See also Release_Store(), above. |
302 | 1.04k | ATOMICOPS_COMPILER_BARRIER(); |
303 | 1.04k | return value; |
304 | 1.04k | } |
305 | | |
306 | 0 | inline Atomic64 Release_Load(volatile const Atomic64* ptr) { |
307 | 0 | CheckNaturalAlignment(ptr); |
308 | 0 | MemoryBarrier(); |
309 | 0 | return *ptr; |
310 | 0 | } |
311 | | |
312 | | #else // defined(__x86_64__) |
313 | | |
314 | | // 64-bit low-level operations on 32-bit platform. |
315 | | |
316 | | #if !((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) |
317 | | // For compilers older than gcc 4.1, we use inline asm. |
318 | | // |
319 | | // Potential pitfalls: |
320 | | // |
321 | | // 1. %ebx points to Global offset table (GOT) with -fPIC. |
322 | | // We need to preserve this register. |
323 | | // 2. When explicit registers are used in inline asm, the |
324 | | // compiler may not be aware of it and might try to reuse |
325 | | // the same register for another argument which has constraints |
326 | | // that allow it ("r" for example). |
327 | | |
328 | | inline Atomic64 __sync_val_compare_and_swap(volatile Atomic64* ptr, Atomic64 old_value, |
329 | | Atomic64 new_value) { |
330 | | CheckNaturalAlignment(ptr); |
331 | | Atomic64 prev; |
332 | | __asm__ __volatile__( |
333 | | "push %%ebx\n\t" |
334 | | "movl (%3), %%ebx\n\t" // Move 64-bit new_value into |
335 | | "movl 4(%3), %%ecx\n\t" // ecx:ebx |
336 | | "lock; cmpxchg8b (%1)\n\t" // If edx:eax (old_value) same |
337 | | "pop %%ebx\n\t" |
338 | | : "=A"(prev) // as contents of ptr: |
339 | | : "D"(ptr), // ecx:ebx => ptr |
340 | | "0"(old_value), // else: |
341 | | "S"(&new_value) // old *ptr => edx:eax |
342 | | : "memory", "%ecx"); |
343 | | return prev; |
344 | | } |
345 | | #endif // Compiler < gcc-4.1 |
346 | | |
347 | | inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_val, |
348 | | Atomic64 new_val) { |
349 | | CheckNaturalAlignment(ptr); |
350 | | return __sync_val_compare_and_swap(ptr, old_val, new_val); |
351 | | } |
352 | | |
353 | | inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_val) { |
354 | | Atomic64 old_val; |
355 | | CheckNaturalAlignment(ptr); |
356 | | |
357 | | do { |
358 | | old_val = *ptr; |
359 | | } while (__sync_val_compare_and_swap(ptr, old_val, new_val) != old_val); |
360 | | |
361 | | return old_val; |
362 | | } |
363 | | |
364 | | inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_val) { |
365 | | CheckNaturalAlignment(ptr); |
366 | | Atomic64 old_val = NoBarrier_AtomicExchange(ptr, new_val); |
367 | | return old_val; |
368 | | } |
369 | | |
370 | | inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_val) { |
371 | | return NoBarrier_AtomicExchange(ptr, new_val); |
372 | | } |
373 | | |
374 | | inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { |
375 | | CheckNaturalAlignment(ptr); |
376 | | Atomic64 old_val, new_val; |
377 | | |
378 | | do { |
379 | | old_val = *ptr; |
380 | | new_val = old_val + increment; |
381 | | } while (__sync_val_compare_and_swap(ptr, old_val, new_val) != old_val); |
382 | | |
383 | | return old_val + increment; |
384 | | } |
385 | | |
386 | | inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { |
387 | | CheckNaturalAlignment(ptr); |
388 | | Atomic64 new_val = NoBarrier_AtomicIncrement(ptr, increment); |
389 | | return new_val; |
390 | | } |
391 | | |
392 | | inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { |
393 | | CheckNaturalAlignment(ptr); |
394 | | __asm__ __volatile__( |
395 | | "movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic |
396 | | "movq %%mm0, %0\n\t" // moves (ptr could be read-only) |
397 | | "emms\n\t" // Empty mmx state/Reset FP regs |
398 | | : "=m"(*ptr) |
399 | | : "m"(value) |
400 | | : // mark the FP stack and mmx registers as clobbered |
401 | | "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", "mm0", "mm1", |
402 | | "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"); |
403 | | } |
404 | | |
405 | | inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { |
406 | | NoBarrier_Store(ptr, value); |
407 | | MemoryBarrier(); |
408 | | } |
409 | | |
410 | | inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { |
411 | | ATOMICOPS_COMPILER_BARRIER(); |
412 | | NoBarrier_Store(ptr, value); |
413 | | } |
414 | | |
415 | | inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { |
416 | | CheckNaturalAlignment(ptr); |
417 | | Atomic64 value; |
418 | | __asm__ __volatile__( |
419 | | "movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic |
420 | | "movq %%mm0, %0\n\t" // moves (ptr could be read-only) |
421 | | "emms\n\t" // Empty mmx state/Reset FP regs |
422 | | : "=m"(value) |
423 | | : "m"(*ptr) |
424 | | : // mark the FP stack and mmx registers as clobbered |
425 | | "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", "mm0", "mm1", |
426 | | "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"); |
427 | | return value; |
428 | | } |
429 | | |
430 | | inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { |
431 | | CheckNaturalAlignment(ptr); |
432 | | Atomic64 value = NoBarrier_Load(ptr); |
433 | | ATOMICOPS_COMPILER_BARRIER(); |
434 | | return value; |
435 | | } |
436 | | |
437 | | inline Atomic64 Release_Load(volatile const Atomic64* ptr) { |
438 | | MemoryBarrier(); |
439 | | return NoBarrier_Load(ptr); |
440 | | } |
441 | | |
442 | | #endif // defined(__x86_64__) |
443 | | |
444 | | inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, |
445 | 0 | Atomic64 new_value) { |
446 | 0 | return NoBarrier_CompareAndSwap(ptr, old_value, new_value); |
447 | 0 | } |
448 | | |
449 | | inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, |
450 | 0 | Atomic64 new_value) { |
451 | 0 | return NoBarrier_CompareAndSwap(ptr, old_value, new_value); |
452 | 0 | } |
453 | | |
454 | | inline Atomic64 Barrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, |
455 | 0 | Atomic64 new_value) { |
456 | 0 | return NoBarrier_CompareAndSwap(ptr, old_value, new_value); |
457 | 0 | } |
458 | | |
459 | | } // namespace subtle |
460 | | } // namespace base |
461 | | |
462 | | #undef ATOMICOPS_COMPILER_BARRIER |