xref: /5.5.2/platform/include/relaxed_atomic.h (revision 8b9a714b)
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
21namespace Couchbase {
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:
30        RelaxedAtomic() {
31            store(0);
32        }
33
34        RelaxedAtomic(const T& initial) {
35            store(initial);
36        }
37
38        explicit RelaxedAtomic(const RelaxedAtomic& other) {
39            store(other.load());
40        }
41
42        operator T() const {
43            return load();
44        }
45
46        T load() const {
47            return value.load(std::memory_order_relaxed);
48        }
49
50        void store(T desired) {
51            value.store(desired, std::memory_order_relaxed);
52        }
53
54        T fetch_add(T arg) {
55            return value.fetch_add(arg, std::memory_order_relaxed);
56        }
57
58        T fetch_sub(T arg) {
59            return value.fetch_sub(arg, std::memory_order_relaxed);
60        }
61
62        RelaxedAtomic& operator=(const RelaxedAtomic& rhs) {
63            store(rhs.load());
64            return *this;
65        }
66
67        RelaxedAtomic& operator+=(const T rhs) {
68            fetch_add(rhs);
69            return *this;
70        }
71
72        RelaxedAtomic& operator+=(const RelaxedAtomic& rhs) {
73            fetch_add(rhs.load());
74            return *this;
75        }
76
77        RelaxedAtomic& operator-=(const T rhs) {
78            fetch_sub(rhs);
79            return *this;
80        }
81
82        RelaxedAtomic& operator-=(const RelaxedAtomic& rhs) {
83            fetch_sub(rhs.load());
84            return *this;
85        }
86
87        T operator++() {
88            return fetch_add(1) + 1;
89        }
90
91        T operator++(int) {
92            return fetch_add(1);
93        }
94
95        T operator--() {
96            return fetch_sub(1) - 1;
97        }
98
99        T operator--(int) {
100            return fetch_sub(1);
101        }
102
103        RelaxedAtomic& operator=(T val) {
104            store(val);
105            return *this;
106        }
107
108        void reset() {
109            store(0);
110        }
111
112        void setIfGreater(const T& val) {
113            do {
114                T currval = load();
115                if (val > currval) {
116                    if (value.compare_exchange_weak(currval, val,
117                                                    std::memory_order_relaxed)) {
118                        break;
119                    }
120                } else {
121                    break;
122                }
123            } while (true);
124        }
125
126        void setIfGreater(const RelaxedAtomic& val) {
127            setIfGreater(val.load());
128        }
129
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
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         */
156        void setAdd(const T& val) {
157            do {
158                T currval = load();
159                if (value.compare_exchange_weak(currval,
160                                                currval + val,
161                                                std::memory_order_relaxed)) {
162                    break;
163                }
164            } while (true);
165        }
166
167        /**
168         * Implementation similar to fetch_add. This function is needed as not
169         * all implementations of RelaxedAtomic are able to make use of the
170         * operator overload methods if they are not of integral type. In the
171         * first case, using the operator overload is preferred as it is more
172         * optimized, this should only be used if that is not possible.
173         * @param val The RelaxedAtomic containing the value to add to that of
174         * the one stored
175         */
176        void setAdd(const RelaxedAtomic& val) {
177            setAdd(val.load());
178        }
179
180        /**
181         * Implementation similar to fetch_sub. This function is needed as not
182         * all implementations of RelaxedAtomic are able to make use of the
183         * operator overload methods if they are not of integral type. In the
184         * first case, using the operator overload is preferred as it is more
185         * optimized, this should only be used if that is not possible.
186         * @param val The value to subtract to that of the one stored
187         */
188        void setSub(const T& val) {
189            do {
190                T currval = load();
191                if (value.compare_exchange_weak(currval,
192                                                currval - val,
193                                                std::memory_order_relaxed)) {
194                    break;
195                }
196            } while (true);
197        }
198
199        /**
200         * Implementation similar to fetch_sub. This function is needed as not
201         * all implementations of RelaxedAtomic are able to make use of the
202         * operator overload methods if they are not of integral type. In the
203         * first case, using the operator overload is preferred as it is more
204         * optimized, this should only be used if that is not possible.
205         * @param val The RelaxedAtomic containing the value to subtract to that
206         * of the one stored
207         */
208        void setSub(const RelaxedAtomic& val) {
209            setSub(val.load());
210        }
211
212        bool compare_exchange_weak(T& expected, T desired) {
213            return value.compare_exchange_weak(expected,
214                                               desired,
215                                               std::memory_order_release,
216                                               std::memory_order_relaxed);
217        }
218
219        T exchange(T desired) {
220            return value.exchange(desired, std::memory_order_relaxed);
221        }
222
223    protected:
224        std::atomic <T> value;
225    };
226}
227