1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This file is an internal atomic implementation, use base/atomicops.h instead.
6//
7// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
8
9#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
10#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
11
12namespace base {
13namespace subtle {
14
15// 0xffff0fc0 is the hard coded address of a function provided by
16// the kernel which implements an atomic compare-exchange. On older
17// ARM architecture revisions (pre-v6) this may be implemented using
18// a syscall. This address is stable, and in active use (hard coded)
19// by at least glibc-2.7 and the Android C library.
20typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value,
21                                           Atomic32 new_value,
22                                           volatile Atomic32* ptr);
23LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) =
24    (LinuxKernelCmpxchgFunc) 0xffff0fc0;
25
26typedef void (*LinuxKernelMemoryBarrierFunc)(void);
27LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
28    (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
29
30
31inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
32                                         Atomic32 old_value,
33                                         Atomic32 new_value) {
34  Atomic32 prev_value = *ptr;
35  do {
36    if (!pLinuxKernelCmpxchg(old_value, new_value,
37                             const_cast<Atomic32*>(ptr))) {
38      return old_value;
39    }
40    prev_value = *ptr;
41  } while (prev_value == old_value);
42  return prev_value;
43}
44
45inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
46                                         Atomic32 new_value) {
47  Atomic32 old_value;
48  do {
49    old_value = *ptr;
50  } while (pLinuxKernelCmpxchg(old_value, new_value,
51                               const_cast<Atomic32*>(ptr)));
52  return old_value;
53}
54
55inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
56                                          Atomic32 increment) {
57  return Barrier_AtomicIncrement(ptr, increment);
58}
59
60inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
61                                        Atomic32 increment) {
62  for (;;) {
63    // Atomic exchange the old value with an incremented one.
64    Atomic32 old_value = *ptr;
65    Atomic32 new_value = old_value + increment;
66    if (pLinuxKernelCmpxchg(old_value, new_value,
67                            const_cast<Atomic32*>(ptr)) == 0) {
68      // The exchange took place as expected.
69      return new_value;
70    }
71    // Otherwise, *ptr changed mid-loop and we need to retry.
72  }
73
74}
75
76inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
77                                       Atomic32 old_value,
78                                       Atomic32 new_value) {
79  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
80}
81
82inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
83                                       Atomic32 old_value,
84                                       Atomic32 new_value) {
85  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
86}
87
88inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
89  *ptr = value;
90}
91
92inline void MemoryBarrier() {
93  pLinuxKernelMemoryBarrier();
94}
95
96inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
97  *ptr = value;
98  MemoryBarrier();
99}
100
101inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
102  MemoryBarrier();
103  *ptr = value;
104}
105
106inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
107  return *ptr;
108}
109
110inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
111  Atomic32 value = *ptr;
112  MemoryBarrier();
113  return value;
114}
115
116inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
117  MemoryBarrier();
118  return *ptr;
119}
120
121} // namespace base::subtle
122} // namespace base
123
124#endif  // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
125