summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Carey <jcarey@argv.me>2019-05-13 18:17:34 -0400
committerJason Carey <jcarey@argv.me>2019-05-22 17:11:39 -0400
commitefd0267a75f0d8de8a451f77b0447484f7d0ee48 (patch)
tree447f3aae12153191d7a6f688c47c96ea402df084
parent5a3bc788f03b02fe121bcf1f81528a74c3114ced (diff)
downloadmongo-efd0267a75f0d8de8a451f77b0447484f7d0ee48.tar.gz
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
-rw-r--r--src/mongo/platform/atomic_word.h48
-rw-r--r--src/mongo/platform/atomic_word_test.cpp24
-rw-r--r--src/mongo/util/fail_point.cpp22
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 <typename T>
constexpr Category getCategory() {
- if (std::is_integral<T>() && !std::is_same<T, bool>())
+ if (std::is_integral<T>() && !std::is_same<T, bool>()) {
+ if (std::is_unsigned<T>() && !std::is_same<T, char>()) {
+ return Category::kUnsigned;
+ }
return Category::kArithmetic;
+ }
return Category::kBasic;
}
@@ -129,7 +133,7 @@ protected:
*/
template <typename T>
class Base<T, Category::kArithmetic> : public Base<T, Category::kBasic> {
-private:
+protected:
using Parent = Base<T, Category::kBasic>;
using Parent::_value;
@@ -179,6 +183,44 @@ public:
}
};
+template <typename T>
+class Base<T, Category::kUnsigned> : public Base<T, Category::kArithmetic> {
+private:
+ using Parent = Base<T, Category::kArithmetic>;
+ 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 <typename AtomicWordType>
+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<T>().fetchAndBitAnd(0));
+ASSERT_DOES_NOT_COMPILE(typename T = int, AtomicWord<T>().fetchAndBitOr(0));
+ASSERT_DOES_NOT_COMPILE(typename T = int, AtomicWord<T>().fetchAndBitXor(0));
+
+ASSERT_DOES_NOT_COMPILE(typename T = char, AtomicWord<T>().fetchAndBitAnd(0));
+ASSERT_DOES_NOT_COMPILE(typename T = char, AtomicWord<T>().fetchAndBitOr(0));
+ASSERT_DOES_NOT_COMPILE(typename T = char, AtomicWord<T>().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<AtomicWord<unsigned>>();
+ testAtomicWordBitOperations<AtomicWord<unsigned>>();
AtomicWord<unsigned> 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<AtomicWord<unsigned long long>>();
+ testAtomicWordBitOperations<AtomicWord<unsigned long long>>();
AtomicWord<unsigned long long> 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(