1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #pragma once
18 
19 #include <atomic>
20 
21 namespace cb {
22 
23 /**
24  * The RelaxedAtomic class wraps std::atomic<> and operates with
25  * relaxed memory ordering.
26  */
27 template <typename T>
28 class RelaxedAtomic {
29 public:
RelaxedAtomic()30     RelaxedAtomic() {
31         store(0);
32     }
33 
RelaxedAtomic(const T& initial)34     RelaxedAtomic(const T& initial) {
35         store(initial);
36     }
37 
RelaxedAtomic(const RelaxedAtomic& other)38     explicit RelaxedAtomic(const RelaxedAtomic& other) {
39         store(other.load());
40     }
41 
operator T() const42     operator T() const {
43         return load();
44     }
45 
load() const46     T load() const {
47         return value.load(std::memory_order_relaxed);
48     }
49 
store(T desired)50     void store(T desired) {
51         value.store(desired, std::memory_order_relaxed);
52     }
53 
fetch_add(T arg)54     T fetch_add(T arg) {
55         return value.fetch_add(arg, std::memory_order_relaxed);
56     }
57 
fetch_sub(T arg)58     T fetch_sub(T arg) {
59         return value.fetch_sub(arg, std::memory_order_relaxed);
60     }
61 
operator =(const RelaxedAtomic& rhs)62     RelaxedAtomic& operator=(const RelaxedAtomic& rhs) {
63         store(rhs.load());
64         return *this;
65     }
66 
operator +=(const T rhs)67     RelaxedAtomic& operator+=(const T rhs) {
68         fetch_add(rhs);
69         return *this;
70     }
71 
operator +=(const RelaxedAtomic& rhs)72     RelaxedAtomic& operator+=(const RelaxedAtomic& rhs) {
73         fetch_add(rhs.load());
74         return *this;
75     }
76 
operator -=(const T rhs)77     RelaxedAtomic& operator-=(const T rhs) {
78         fetch_sub(rhs);
79         return *this;
80     }
81 
operator -=(const RelaxedAtomic& rhs)82     RelaxedAtomic& operator-=(const RelaxedAtomic& rhs) {
83         fetch_sub(rhs.load());
84         return *this;
85     }
86 
operator ++()87     T operator++() {
88         return fetch_add(1) + 1;
89     }
90 
operator ++(int)91     T operator++(int) {
92         return fetch_add(1);
93     }
94 
operator --()95     T operator--() {
96         return fetch_sub(1) - 1;
97     }
98 
operator --(int)99     T operator--(int) {
100         return fetch_sub(1);
101     }
102 
operator =(T val)103     RelaxedAtomic& operator=(T val) {
104         store(val);
105         return *this;
106     }
107 
reset()108     void reset() {
109         store(0);
110     }
111 
setIfGreater(const T& val)112     void setIfGreater(const T& val) {
113         do {
114             T currval = load();
115             if (val > currval) {
116                 if (value.compare_exchange_weak(
117                             currval, val, std::memory_order_relaxed)) {
118                     break;
119                 }
120             } else {
121                 break;
122             }
123         } while (true);
124     }
125 
setIfGreater(const RelaxedAtomic& val)126     void setIfGreater(const RelaxedAtomic& val) {
127         setIfGreater(val.load());
128     }
129 
setIfSmaller(const T& val)130     void setIfSmaller(const T& val) {
131         do {
132             T currval = load();
133             if (val < currval) {
134                 if (value.compare_exchange_weak(
135                             currval, val, std::memory_order_relaxed)) {
136                     break;
137                 }
138             } else {
139                 break;
140             }
141         } while (true);
142     }
143 
setIfSmaller(const RelaxedAtomic& val)144     void setIfSmaller(const RelaxedAtomic& val) {
145         setIfSmaller(val.load());
146     }
147 
148     /**
149      * Implementation similar to fetch_add. This function is needed as not
150      * all implementations of RelaxedAtomic are able to make use of the
151      * operator overload methods if they are not of integral type. In the
152      * first case, using the operator overload is preferred as it is more
153      * optimized, this should only be used if that is not possible.
154      * @param val The value to add to that of the one stored
155      */
setAdd(const T& val)156     void setAdd(const T& val) {
157         do {
158             T currval = load();
159             if (value.compare_exchange_weak(
160                         currval, currval + val, std::memory_order_relaxed)) {
161                 break;
162             }
163         } while (true);
164     }
165 
166     /**
167      * Implementation similar to fetch_add. This function is needed as not
168      * all implementations of RelaxedAtomic are able to make use of the
169      * operator overload methods if they are not of integral type. In the
170      * first case, using the operator overload is preferred as it is more
171      * optimized, this should only be used if that is not possible.
172      * @param val The RelaxedAtomic containing the value to add to that of
173      * the one stored
174      */
setAdd(const RelaxedAtomic& val)175     void setAdd(const RelaxedAtomic& val) {
176         setAdd(val.load());
177     }
178 
179     /**
180      * Implementation similar to fetch_sub. This function is needed as not
181      * all implementations of RelaxedAtomic are able to make use of the
182      * operator overload methods if they are not of integral type. In the
183      * first case, using the operator overload is preferred as it is more
184      * optimized, this should only be used if that is not possible.
185      * @param val The value to subtract to that of the one stored
186      */
setSub(const T& val)187     void setSub(const T& val) {
188         do {
189             T currval = load();
190             if (value.compare_exchange_weak(
191                         currval, currval - val, std::memory_order_relaxed)) {
192                 break;
193             }
194         } while (true);
195     }
196 
197     /**
198      * Implementation similar to fetch_sub. This function is needed as not
199      * all implementations of RelaxedAtomic are able to make use of the
200      * operator overload methods if they are not of integral type. In the
201      * first case, using the operator overload is preferred as it is more
202      * optimized, this should only be used if that is not possible.
203      * @param val The RelaxedAtomic containing the value to subtract to that
204      * of the one stored
205      */
setSub(const RelaxedAtomic& val)206     void setSub(const RelaxedAtomic& val) {
207         setSub(val.load());
208     }
209 
compare_exchange_weak(T& expected, T desired)210     bool compare_exchange_weak(T& expected, T desired) {
211         return value.compare_exchange_weak(expected,
212                                            desired,
213                                            std::memory_order_release,
214                                            std::memory_order_relaxed);
215     }
216 
exchange(T desired)217     T exchange(T desired) {
218         return value.exchange(desired, std::memory_order_relaxed);
219     }
220 
221 private:
222     std::atomic<T> value;
223 };
224 } // namespace cb
225