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