1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <atomic>
34 #include <cstring>
35 #include <type_traits>
36 
37 #include "mongo/base/static_assert.h"
38 #include "mongo/stdx/type_traits.h"
39 
40 namespace mongo {
41 
42 /**
43  * Instantiations of AtomicWord must be Integral, or Trivally Copyable and less than 8 bytes.
44  */
45 template <typename _WordType, typename = void>
46 class AtomicWord;
47 
48 /**
49  * Implementation of the AtomicWord interface in terms of the C++11 Atomics.
50  */
51 template <typename _WordType>
52 class AtomicWord<_WordType, stdx::enable_if_t<std::is_integral<_WordType>::value>> {
53 public:
54     /**
55      * Underlying value type.
56      */
57     typedef _WordType WordType;
58 
59     /**
60      * Construct a new word with the given initial value.
61      */
_value(value)62     explicit constexpr AtomicWord(WordType value = WordType(0)) : _value(value) {}
63 
64     /**
65      * Gets the current value of this AtomicWord.
66      *
67      * Has acquire and release semantics.
68      */
load()69     WordType load() const {
70         return _value.load();
71     }
72 
73     /**
74      * Gets the current value of this AtomicWord.
75      *
76      * Has relaxed semantics.
77      */
loadRelaxed()78     WordType loadRelaxed() const {
79         return _value.load(std::memory_order_relaxed);
80     }
81 
82     /**
83      * Sets the value of this AtomicWord to "newValue".
84      *
85      * Has acquire and release semantics.
86      */
store(WordType newValue)87     void store(WordType newValue) {
88         return _value.store(newValue);
89     }
90 
91     /**
92      * Atomically swaps the current value of this with "newValue".
93      *
94      * Returns the old value.
95      *
96      * Has acquire and release semantics.
97      */
swap(WordType newValue)98     WordType swap(WordType newValue) {
99         return _value.exchange(newValue);
100     }
101 
102     /**
103      * Atomic compare and swap.
104      *
105      * If this value equals "expected", sets this to "newValue".
106      * Always returns the original of this.
107      *
108      * Has acquire and release semantics.
109      */
compareAndSwap(WordType expected,WordType newValue)110     WordType compareAndSwap(WordType expected, WordType newValue) {
111         // NOTE: Subtle: compare_exchange mutates its first argument.
112         _value.compare_exchange_strong(expected, newValue);
113         return expected;
114     }
115 
116     /**
117      * Get the current value of this, add "increment" and store it, atomically.
118      *
119      * Returns the value of this before incrementing.
120      *
121      * Has acquire and release semantics.
122      */
fetchAndAdd(WordType increment)123     WordType fetchAndAdd(WordType increment) {
124         return _value.fetch_add(increment);
125     }
126 
127     /**
128      * Get the current value of this, subtract "decrement" and store it, atomically.
129      *
130      * Returns the value of this before decrementing.
131      *
132      * Has acquire and release semantics.
133      */
fetchAndSubtract(WordType decrement)134     WordType fetchAndSubtract(WordType decrement) {
135         return _value.fetch_sub(decrement);
136     }
137 
138     /**
139      * Get the current value of this, add "increment" and store it, atomically.
140      *
141      * Returns the value of this after incrementing.
142      *
143      * Has acquire and release semantics.
144      */
addAndFetch(WordType increment)145     WordType addAndFetch(WordType increment) {
146         return fetchAndAdd(increment) + increment;
147     }
148 
149     /**
150      * Get the current value of this, subtract "decrement" and store it, atomically.
151      *
152      * Returns the value of this after decrementing.
153      *
154      * Has acquire and release semantics.
155      */
subtractAndFetch(WordType decrement)156     WordType subtractAndFetch(WordType decrement) {
157         return fetchAndSubtract(decrement) - decrement;
158     }
159 
160 private:
161     std::atomic<WordType> _value;  // NOLINT
162 };
163 
164 /**
165  * Implementation of the AtomicWord interface for non-integral types that are trivially copyable and
166  * fit in 8 bytes.  For that implementation we flow reads and writes through memcpy'ing bytes in and
167  * out of a uint64_t, then relying on std::atomic<uint64_t>.
168  */
169 template <typename _WordType>
170 class AtomicWord<_WordType,
171                  stdx::enable_if_t<sizeof(_WordType) <= sizeof(uint64_t) &&
172                                    !std::is_integral<_WordType>::value &&
173                                    std::is_trivially_copyable<_WordType>::value>> {
174     using StorageType = uint64_t;
175 
176 public:
177     /**
178      * Underlying value type.
179      */
180     typedef _WordType WordType;
181 
182     /**
183      * Construct a new word with the given initial value.
184      */
185     explicit AtomicWord(WordType value = WordType{}) {
186         store(value);
187     }
188 
189     // Used in invoking a zero'd out non-integral atomic word
190     struct ZeroInitTag {};
191     /**
192      * Construct a new word with zero'd out bytes.  Useful if you need a constexpr AtomicWord of
193      * non-integral type.
194      */
AtomicWord(ZeroInitTag)195     constexpr explicit AtomicWord(ZeroInitTag) : _storage(0) {}
196 
197     /**
198      * Gets the current value of this AtomicWord.
199      *
200      * Has acquire and release semantics.
201      */
load()202     WordType load() const {
203         return _fromStorage(_storage.load());
204     }
205 
206     /**
207      * Gets the current value of this AtomicWord.
208      *
209      * Has relaxed semantics.
210      */
loadRelaxed()211     WordType loadRelaxed() const {
212         return _fromStorage(_storage.load(std::memory_order_relaxed));
213     }
214 
215     /**
216      * Sets the value of this AtomicWord to "newValue".
217      *
218      * Has acquire and release semantics.
219      */
store(WordType newValue)220     void store(WordType newValue) {
221         _storage.store(_toStorage(newValue));
222     }
223 
224     /**
225      * Atomically swaps the current value of this with "newValue".
226      *
227      * Returns the old value.
228      *
229      * Has acquire and release semantics.
230      */
swap(WordType newValue)231     WordType swap(WordType newValue) {
232         return _fromStorage(_storage.exchange(_toStorage(newValue)));
233     }
234 
235     /**
236      * Atomic compare and swap.
237      *
238      * If this value equals "expected", sets this to "newValue".
239      * Always returns the original of this.
240      *
241      * Has acquire and release semantics.
242      */
compareAndSwap(WordType expected,WordType newValue)243     WordType compareAndSwap(WordType expected, WordType newValue) {
244         // NOTE: Subtle: compare_exchange mutates its first argument.
245         auto v = _toStorage(expected);
246         _storage.compare_exchange_strong(v, _toStorage(newValue));
247         return _fromStorage(v);
248     }
249 
250 private:
_fromStorage(StorageType storage)251     static WordType _fromStorage(StorageType storage) noexcept {
252         WordType v;
253         std::memcpy(&v, &storage, sizeof(v));
254         return v;
255     }
256 
_toStorage(WordType wordType)257     static StorageType _toStorage(WordType wordType) noexcept {
258         StorageType v = 0;
259         std::memcpy(&v, &wordType, sizeof(wordType));
260         return v;
261     }
262 
263     std::atomic<StorageType> _storage;  // NOLINT
264 };
265 
266 #define _ATOMIC_WORD_DECLARE(NAME, WTYPE)                       \
267     typedef class AtomicWord<WTYPE> NAME;                       \
268     namespace {                                                 \
269     MONGO_STATIC_ASSERT(sizeof(NAME) == sizeof(WTYPE));         \
270     MONGO_STATIC_ASSERT(std::is_standard_layout<WTYPE>::value); \
271     }  // namespace
272 
273 _ATOMIC_WORD_DECLARE(AtomicUInt32, unsigned);
274 _ATOMIC_WORD_DECLARE(AtomicUInt64, unsigned long long);
275 _ATOMIC_WORD_DECLARE(AtomicInt32, int);
276 _ATOMIC_WORD_DECLARE(AtomicInt64, long long);
277 _ATOMIC_WORD_DECLARE(AtomicBool, bool);
278 #undef _ATOMIC_WORD_DECLARE
279 
280 }  // namespace mongo
281