Clean up Conv.cpp / Conv.h
[folly.git] / folly / test / DeterministicScheduleTest.cpp
index ceca71e383f8f0e0834ab52e78e318577c15a747..a7ded40633c7dca5cbd1326973d2c8164983455f 100644 (file)
@@ -52,6 +52,61 @@ TEST(DeterministicSchedule, uniformSubset) {
   }
 }
 
+TEST(DeterministicSchedule, buggyAdd) {
+  for (bool bug : {false, true}) {
+    DeterministicSchedule sched(DeterministicSchedule::uniform(0));
+    if (bug) {
+      FOLLY_TEST_DSCHED_VLOG("Test with race condition");
+    } else {
+      FOLLY_TEST_DSCHED_VLOG("Test without race condition");
+    }
+    DeterministicMutex m;
+    // The use of DeterinisticAtomic is not needed here, but it makes
+    // it easier to understand the sequence of events in logs.
+    DeterministicAtomic<int> test{0};
+    DeterministicAtomic<int> baseline{0};
+    int numThreads = 10;
+    std::vector<std::thread> threads(numThreads);
+    for (int t = 0; t < numThreads; ++t) {
+      threads[t] = DeterministicSchedule::thread([&, t] {
+        baseline.fetch_add(1);
+        // Atomic increment of test protected by mutex m
+        do {
+          // Some threads use lock() others use try_lock()
+          if ((t & 1) == 0) {
+            m.lock();
+          } else {
+            if (!m.try_lock()) {
+              continue;
+            }
+          }
+          int newval = test.load() + 1;
+          if (bug) {
+            // Break the atomicity of the increment operation
+            m.unlock();
+            m.lock();
+          }
+          test.store(newval);
+          m.unlock();
+          break;
+        } while (true);
+      }); // thread lambda
+    } // for t
+    for (auto& t : threads) {
+      DeterministicSchedule::join(t);
+    }
+    if (!bug) {
+      EXPECT_EQ(test.load(), baseline.load());
+    } else {
+      if (test.load() == baseline.load()) {
+        FOLLY_TEST_DSCHED_VLOG("Didn't catch the bug");
+      } else {
+        FOLLY_TEST_DSCHED_VLOG("Caught the bug");
+      }
+    }
+  } // for bug
+} // TEST
+
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
   gflags::ParseCommandLineFlags(&argc, &argv, true);