From: Yedidya Feldblum Date: Tue, 26 Dec 2017 01:07:39 +0000 (-0800) Subject: Move folly/AtomicStruct.h to folly/synchronization/ X-Git-Tag: v2018.01.01.00~21 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=078d29f70bac3893084ae1183560a29c8afdb028;hp=c2d5c9043cbf92da12a996be80a94ed57c96a9aa;p=folly.git Move folly/AtomicStruct.h to folly/synchronization/ Summary: [Folly] Move `folly/AtomicStruct.h` to `folly/synchronization/`. Reviewed By: Orvid Differential Revision: D6636072 fbshipit-source-id: 87098e25fc40d0d60b4a657ba395418388e170fc --- diff --git a/folly/AtomicStruct.h b/folly/AtomicStruct.h deleted file mode 100644 index a774d315..00000000 --- a/folly/AtomicStruct.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2017 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace folly { - -namespace detail { -template struct AtomicStructIntPick {}; -} // namespace detail - -/// AtomicStruct work like C++ atomics, but can be used on any POD -/// type <= 8 bytes. -template < - typename T, - template class Atom = std::atomic, - typename Raw = typename detail::AtomicStructIntPick::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::value || - folly::IsTriviallyCopyable::value, - "target type must be trivially copyable"); - - union { - Atom data; - T typedData; - }; - - 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 const &) = delete; - AtomicStruct& operator= (AtomicStruct const &) = delete; - - constexpr /* implicit */ AtomicStruct(T v) noexcept : typedData(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 { - return compare_exchange_strong( - v0, v1, mo, detail::default_failure_memory_order(mo)); - } - bool compare_exchange_strong( - T& v0, - T v1, - std::memory_order success, - std::memory_order failure) noexcept { - Raw d0 = encode(v0); - bool rv = data.compare_exchange_strong(d0, encode(v1), success, failure); - 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 { - return compare_exchange_weak( - v0, v1, mo, detail::default_failure_memory_order(mo)); - } - bool compare_exchange_weak( - T& v0, - T v1, - std::memory_order success, - std::memory_order failure) noexcept { - Raw d0 = encode(v0); - bool rv = data.compare_exchange_weak(d0, encode(v1), success, failure); - 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 diff --git a/folly/IndexedMemPool.h b/folly/IndexedMemPool.h index cab5cac1..14661dd7 100644 --- a/folly/IndexedMemPool.h +++ b/folly/IndexedMemPool.h @@ -23,11 +23,11 @@ #include #include -#include #include #include #include #include +#include // Ignore shadowing warnings within this file, so includers can use -Wshadow. FOLLY_PUSH_WARNING diff --git a/folly/Makefile.am b/folly/Makefile.am index 6df61c3c..ec1e2e67 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -34,7 +34,6 @@ nobase_follyinclude_HEADERS = \ AtomicHashMap-inl.h \ AtomicIntrusiveLinkedList.h \ AtomicLinkedList.h \ - AtomicStruct.h \ AtomicUnorderedMap.h \ Benchmark.h \ Bits.h \ @@ -436,6 +435,7 @@ nobase_follyinclude_HEADERS = \ stats/TimeseriesHistogram-defs.h \ stats/TimeseriesHistogram.h \ synchronization/AsymmetricMemoryBarrier.h \ + synchronization/AtomicStruct.h \ synchronization/Baton.h \ synchronization/CallOnce.h \ synchronization/LifoSem.h \ diff --git a/folly/concurrency/AtomicSharedPtr.h b/folly/concurrency/AtomicSharedPtr.h index d1b2aa7a..7b2e697e 100644 --- a/folly/concurrency/AtomicSharedPtr.h +++ b/folly/concurrency/AtomicSharedPtr.h @@ -15,9 +15,9 @@ */ #pragma once -#include #include #include +#include #include #include #include diff --git a/folly/detail/MemoryIdler.h b/folly/detail/MemoryIdler.h index 1cabfecc..1a924b57 100644 --- a/folly/detail/MemoryIdler.h +++ b/folly/detail/MemoryIdler.h @@ -19,10 +19,10 @@ #include #include -#include #include #include #include +#include #include namespace folly { diff --git a/folly/synchronization/AtomicStruct.h b/folly/synchronization/AtomicStruct.h new file mode 100644 index 00000000..a774d315 --- /dev/null +++ b/folly/synchronization/AtomicStruct.h @@ -0,0 +1,158 @@ +/* + * Copyright 2017 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace folly { + +namespace detail { +template struct AtomicStructIntPick {}; +} // namespace detail + +/// AtomicStruct work like C++ atomics, but can be used on any POD +/// type <= 8 bytes. +template < + typename T, + template class Atom = std::atomic, + typename Raw = typename detail::AtomicStructIntPick::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::value || + folly::IsTriviallyCopyable::value, + "target type must be trivially copyable"); + + union { + Atom data; + T typedData; + }; + + 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 const &) = delete; + AtomicStruct& operator= (AtomicStruct const &) = delete; + + constexpr /* implicit */ AtomicStruct(T v) noexcept : typedData(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 { + return compare_exchange_strong( + v0, v1, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_strong( + T& v0, + T v1, + std::memory_order success, + std::memory_order failure) noexcept { + Raw d0 = encode(v0); + bool rv = data.compare_exchange_strong(d0, encode(v1), success, failure); + 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 { + return compare_exchange_weak( + v0, v1, mo, detail::default_failure_memory_order(mo)); + } + bool compare_exchange_weak( + T& v0, + T v1, + std::memory_order success, + std::memory_order failure) noexcept { + Raw d0 = encode(v0); + bool rv = data.compare_exchange_weak(d0, encode(v1), success, failure); + 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 diff --git a/folly/synchronization/LifoSem.h b/folly/synchronization/LifoSem.h index b06ec4ca..102154cf 100644 --- a/folly/synchronization/LifoSem.h +++ b/folly/synchronization/LifoSem.h @@ -23,10 +23,10 @@ #include #include -#include #include #include #include +#include #include namespace folly { diff --git a/folly/synchronization/test/AtomicStructTest.cpp b/folly/synchronization/test/AtomicStructTest.cpp new file mode 100644 index 00000000..59d3467a --- /dev/null +++ b/folly/synchronization/test/AtomicStructTest.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2017 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. + */ + +#include + +#include + +using namespace folly; + +struct TwoBy32 { + uint32_t left; + uint32_t right; +}; + +TEST(AtomicStruct, two_by_32) { + AtomicStruct a(TwoBy32{ 10, 20 }); + TwoBy32 av = a; + EXPECT_EQ(av.left, 10); + EXPECT_EQ(av.right, 20); + EXPECT_TRUE(a.compare_exchange_strong(av, TwoBy32{ 30, 40 })); + EXPECT_FALSE(a.compare_exchange_weak(av, TwoBy32{ 31, 41 })); + EXPECT_EQ(av.left, 30); + EXPECT_TRUE(a.is_lock_free()); + auto b = a.exchange(TwoBy32{ 50, 60 }); + EXPECT_EQ(b.left, 30); + EXPECT_EQ(b.right, 40); + EXPECT_EQ(a.load().left, 50); + a = TwoBy32{ 70, 80 }; + EXPECT_EQ(a.load().right, 80); + a.store(TwoBy32{ 90, 100 }); + av = a; + EXPECT_EQ(av.left, 90); + AtomicStruct c; + c = b; + EXPECT_EQ(c.load().right, 40); +} + +TEST(AtomicStruct, size_selection) { + struct S1 { char x[1]; }; + struct S2 { char x[2]; }; + struct S3 { char x[3]; }; + struct S4 { char x[4]; }; + struct S5 { char x[5]; }; + struct S6 { char x[6]; }; + struct S7 { char x[7]; }; + struct S8 { char x[8]; }; + + EXPECT_EQ(sizeof(AtomicStruct), 1); + EXPECT_EQ(sizeof(AtomicStruct), 2); + EXPECT_EQ(sizeof(AtomicStruct), 4); + EXPECT_EQ(sizeof(AtomicStruct), 4); + EXPECT_EQ(sizeof(AtomicStruct), 8); + EXPECT_EQ(sizeof(AtomicStruct), 8); + EXPECT_EQ(sizeof(AtomicStruct), 8); + EXPECT_EQ(sizeof(AtomicStruct), 8); +} diff --git a/folly/test/AtomicStructTest.cpp b/folly/test/AtomicStructTest.cpp deleted file mode 100644 index 507f164c..00000000 --- a/folly/test/AtomicStructTest.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 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. - */ - -#include - -#include - -using namespace folly; - -struct TwoBy32 { - uint32_t left; - uint32_t right; -}; - -TEST(AtomicStruct, two_by_32) { - AtomicStruct a(TwoBy32{ 10, 20 }); - TwoBy32 av = a; - EXPECT_EQ(av.left, 10); - EXPECT_EQ(av.right, 20); - EXPECT_TRUE(a.compare_exchange_strong(av, TwoBy32{ 30, 40 })); - EXPECT_FALSE(a.compare_exchange_weak(av, TwoBy32{ 31, 41 })); - EXPECT_EQ(av.left, 30); - EXPECT_TRUE(a.is_lock_free()); - auto b = a.exchange(TwoBy32{ 50, 60 }); - EXPECT_EQ(b.left, 30); - EXPECT_EQ(b.right, 40); - EXPECT_EQ(a.load().left, 50); - a = TwoBy32{ 70, 80 }; - EXPECT_EQ(a.load().right, 80); - a.store(TwoBy32{ 90, 100 }); - av = a; - EXPECT_EQ(av.left, 90); - AtomicStruct c; - c = b; - EXPECT_EQ(c.load().right, 40); -} - -TEST(AtomicStruct, size_selection) { - struct S1 { char x[1]; }; - struct S2 { char x[2]; }; - struct S3 { char x[3]; }; - struct S4 { char x[4]; }; - struct S5 { char x[5]; }; - struct S6 { char x[6]; }; - struct S7 { char x[7]; }; - struct S8 { char x[8]; }; - - EXPECT_EQ(sizeof(AtomicStruct), 1); - EXPECT_EQ(sizeof(AtomicStruct), 2); - EXPECT_EQ(sizeof(AtomicStruct), 4); - EXPECT_EQ(sizeof(AtomicStruct), 4); - EXPECT_EQ(sizeof(AtomicStruct), 8); - EXPECT_EQ(sizeof(AtomicStruct), 8); - EXPECT_EQ(sizeof(AtomicStruct), 8); - EXPECT_EQ(sizeof(AtomicStruct), 8); -}