From efd0267a75f0d8de8a451f77b0447484f7d0ee48 Mon Sep 17 00:00:00 2001 From: Jason Carey Date: Mon, 13 May 2019 18:17:34 -0400 Subject: SERVER-41130 Add support for bitops in AtomicWord AtomicWord offers a slightly less dangerous, doesn't require lint, access to std::atomic's. While that interface currently doesn't offer access to bit operations, the extra cognitive load to understand those correctly is substantially simpler than the main thing AtomicWord hides (access to non sequential consistency). Thus, let's add bitwise operators for unsigned atomic words --- src/mongo/platform/atomic_word.h | 48 ++++++++++++++++++++++++++++++--- src/mongo/platform/atomic_word_test.cpp | 24 +++++++++++++++++ src/mongo/util/fail_point.cpp | 22 ++------------- 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/mongo/platform/atomic_word.h b/src/mongo/platform/atomic_word.h index 8673b063f9b..30a597edcd1 100644 --- a/src/mongo/platform/atomic_word.h +++ b/src/mongo/platform/atomic_word.h @@ -40,12 +40,16 @@ namespace mongo { namespace atomic_word_detail { -enum class Category { kBasic, kArithmetic }; +enum class Category { kBasic, kArithmetic, kUnsigned }; template constexpr Category getCategory() { - if (std::is_integral() && !std::is_same()) + if (std::is_integral() && !std::is_same()) { + if (std::is_unsigned() && !std::is_same()) { + return Category::kUnsigned; + } return Category::kArithmetic; + } return Category::kBasic; } @@ -129,7 +133,7 @@ protected: */ template class Base : public Base { -private: +protected: using Parent = Base; using Parent::_value; @@ -179,6 +183,44 @@ public: } }; +template +class Base : public Base { +private: + using Parent = Base; + using Parent::_value; + +public: + using WordType = typename Parent::WordType; + using Parent::Parent; + + /** + * Atomically compute and store 'load() & bits' + * + * Returns the value of this before bitand-ing. + */ + WordType fetchAndBitAnd(WordType bits) { + return _value.fetch_and(bits); + } + + /** + * Atomically compute and store 'load() | bits' + * + * Returns the value of this before bitor-ing. + */ + WordType fetchAndBitOr(WordType bits) { + return _value.fetch_or(bits); + } + + /** + * Atomically compute and store 'load() ^ bits' + * + * Returns the value of this before bitxor-ing. + */ + WordType fetchAndBitXor(WordType bits) { + return _value.fetch_xor(bits); + } +}; + } // namespace atomic_word_detail /** diff --git a/src/mongo/platform/atomic_word_test.cpp b/src/mongo/platform/atomic_word_test.cpp index 01449192821..212d34f2d13 100644 --- a/src/mongo/platform/atomic_word_test.cpp +++ b/src/mongo/platform/atomic_word_test.cpp @@ -64,6 +64,28 @@ void testAtomicWordBasicOperations() { ASSERT_EQUALS(WordType(0), w.load()); } +template +void testAtomicWordBitOperations() { + typedef typename AtomicWordType::WordType WordType; + AtomicWordType w; + + WordType highBit = 1ull << ((sizeof(WordType) * 8) - 1); + + w.store(highBit | 0xFFull); + ASSERT_EQUALS(WordType(highBit | 0xFFull), w.fetchAndBitAnd(highBit)); + ASSERT_EQUALS(WordType(highBit), w.fetchAndBitOr(highBit | 0xFFull)); + ASSERT_EQUALS(WordType(highBit | 0xFFull), w.fetchAndBitXor(0xFFFF)); + ASSERT_EQUALS(WordType(highBit | 0xFF00ull), w.load()); +} + +ASSERT_DOES_NOT_COMPILE(typename T = int, AtomicWord().fetchAndBitAnd(0)); +ASSERT_DOES_NOT_COMPILE(typename T = int, AtomicWord().fetchAndBitOr(0)); +ASSERT_DOES_NOT_COMPILE(typename T = int, AtomicWord().fetchAndBitXor(0)); + +ASSERT_DOES_NOT_COMPILE(typename T = char, AtomicWord().fetchAndBitAnd(0)); +ASSERT_DOES_NOT_COMPILE(typename T = char, AtomicWord().fetchAndBitOr(0)); +ASSERT_DOES_NOT_COMPILE(typename T = char, AtomicWord().fetchAndBitXor(0)); + enum TestEnum { E0, E1, E2, E3 }; TEST(AtomicWordTests, BasicOperationsEnum) { @@ -79,6 +101,7 @@ TEST(AtomicWordTests, BasicOperationsEnum) { TEST(AtomicWordTests, BasicOperationsUnsigned32Bit) { typedef unsigned WordType; testAtomicWordBasicOperations>(); + testAtomicWordBitOperations>(); AtomicWord w(0xdeadbeef); ASSERT_EQUALS(WordType(0xdeadbeef), w.compareAndSwap(0, 1)); @@ -91,6 +114,7 @@ TEST(AtomicWordTests, BasicOperationsUnsigned32Bit) { TEST(AtomicWordTests, BasicOperationsUnsigned64Bit) { typedef unsigned long long WordType; testAtomicWordBasicOperations>(); + testAtomicWordBitOperations>(); AtomicWord w(0xdeadbeefcafe1234ULL); ASSERT_EQUALS(WordType(0xdeadbeefcafe1234ULL), w.compareAndSwap(0, 1)); diff --git a/src/mongo/util/fail_point.cpp b/src/mongo/util/fail_point.cpp index 9aac1298b07..81da6d94723 100644 --- a/src/mongo/util/fail_point.cpp +++ b/src/mongo/util/fail_point.cpp @@ -117,29 +117,11 @@ const BSONObj& FailPoint::getData() const { } void FailPoint::enableFailPoint() { - // TODO: Better to replace with a bitwise OR, once available for AU32 - ValType currentVal = _fpInfo.load(); - ValType expectedCurrentVal; - ValType newVal; - - do { - expectedCurrentVal = currentVal; - newVal = expectedCurrentVal | ACTIVE_BIT; - currentVal = _fpInfo.compareAndSwap(expectedCurrentVal, newVal); - } while (expectedCurrentVal != currentVal); + _fpInfo.fetchAndBitOr(ACTIVE_BIT); } void FailPoint::disableFailPoint() { - // TODO: Better to replace with a bitwise AND, once available for AU32 - ValType currentVal = _fpInfo.load(); - ValType expectedCurrentVal; - ValType newVal; - - do { - expectedCurrentVal = currentVal; - newVal = expectedCurrentVal & REF_COUNTER_MASK; - currentVal = _fpInfo.compareAndSwap(expectedCurrentVal, newVal); - } while (expectedCurrentVal != currentVal); + _fpInfo.fetchAndBitAnd(~ACTIVE_BIT); } FailPoint::RetCode FailPoint::slowShouldFailOpenBlock( -- cgit v1.2.1