| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef FOLLY_ATOMIC_STRUCT_H_ |
| #define FOLLY_ATOMIC_STRUCT_H_ |
| |
| #include <atomic> |
| #include <type_traits> |
| #include <folly/Traits.h> |
| #include <string.h> |
| #include <stdint.h> |
| |
| namespace folly { |
| |
| namespace detail { |
| template <int N> struct AtomicStructIntPick {}; |
| } |
| |
| /// AtomicStruct<T> work like C++ atomics, but can be used on any POD |
| /// type <= 8 bytes. |
| template < |
| typename T, |
| template<typename> class Atom = std::atomic, |
| typename Raw = typename detail::AtomicStructIntPick<sizeof(T)>::type> |
| class AtomicStruct { |
| static_assert(alignof(T) <= alignof(Raw), |
| "target type can't have stricter alignment than matching int"); |
| static_assert(sizeof(T) <= sizeof(Raw), |
| "underlying type isn't big enough"); |
| static_assert(std::is_trivial<T>::value || |
| folly::IsTriviallyCopyable<T>::value, |
| "target type must be trivially copyable"); |
| |
| Atom<Raw> data; |
| |
| static Raw encode(T v) noexcept { |
| // we expect the compiler to optimize away the memcpy, but without |
| // it we would violate strict aliasing rules |
| Raw d = 0; |
| memcpy(&d, &v, sizeof(T)); |
| return d; |
| } |
| |
| static T decode(Raw d) noexcept { |
| T v; |
| memcpy(&v, &d, sizeof(T)); |
| return v; |
| } |
| |
| public: |
| AtomicStruct() = default; |
| ~AtomicStruct() = default; |
| AtomicStruct(AtomicStruct<T> const &) = delete; |
| AtomicStruct<T>& operator= (AtomicStruct<T> const &) = delete; |
| |
| constexpr /* implicit */ AtomicStruct(T v) noexcept : data(encode(v)) {} |
| |
| bool is_lock_free() const noexcept { |
| return data.is_lock_free(); |
| } |
| |
| bool compare_exchange_strong( |
| T& v0, T v1, |
| std::memory_order mo = std::memory_order_seq_cst) noexcept { |
| Raw d0 = encode(v0); |
| bool rv = data.compare_exchange_strong(d0, encode(v1), mo); |
| if (!rv) { |
| v0 = decode(d0); |
| } |
| return rv; |
| } |
| |
| bool compare_exchange_weak( |
| T& v0, T v1, |
| std::memory_order mo = std::memory_order_seq_cst) noexcept { |
| Raw d0 = encode(v0); |
| bool rv = data.compare_exchange_weak(d0, encode(v1), mo); |
| if (!rv) { |
| v0 = decode(d0); |
| } |
| return rv; |
| } |
| |
| T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept { |
| return decode(data.exchange(encode(v), mo)); |
| } |
| |
| /* implicit */ operator T () const noexcept { |
| return decode(data); |
| } |
| |
| T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept { |
| return decode(data.load(mo)); |
| } |
| |
| T operator= (T v) noexcept { |
| return decode(data = encode(v)); |
| } |
| |
| void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept { |
| data.store(encode(v), mo); |
| } |
| |
| // std::atomic also provides volatile versions of all of the access |
| // methods. These are callable on volatile objects, and also can |
| // theoretically have different implementations than their non-volatile |
| // counterpart. If someone wants them here they can easily be added |
| // by duplicating the above code and the corresponding unit tests. |
| }; |
| |
| namespace detail { |
| |
| template <> struct AtomicStructIntPick<1> { typedef uint8_t type; }; |
| template <> struct AtomicStructIntPick<2> { typedef uint16_t type; }; |
| template <> struct AtomicStructIntPick<3> { typedef uint32_t type; }; |
| template <> struct AtomicStructIntPick<4> { typedef uint32_t type; }; |
| template <> struct AtomicStructIntPick<5> { typedef uint64_t type; }; |
| template <> struct AtomicStructIntPick<6> { typedef uint64_t type; }; |
| template <> struct AtomicStructIntPick<7> { typedef uint64_t type; }; |
| template <> struct AtomicStructIntPick<8> { typedef uint64_t type; }; |
| |
| } // namespace detail |
| |
| } // namespace folly |
| |
| #endif |