Test of DeterministicSchedule support for global invariants and auxiliary vari
[folly.git] / folly / test / DeterministicScheduleTest.cpp
index a7ded40633c7dca5cbd1326973d2c8164983455f..36654856df5a70a12209f65f7f1b8e20959917fa 100644 (file)
@@ -107,6 +107,95 @@ TEST(DeterministicSchedule, buggyAdd) {
   } // for bug
 } // TEST
 
+/// Testing support for auxiliary variables and global invariants
+
+/** auxiliary variables for atomic counter test */
+struct AtomicCounterAux {
+  std::vector<int> local_;
+
+  explicit AtomicCounterAux(int nthr) {
+    local_.resize(nthr, 0);
+  }
+};
+
+/** auxiliary function for checking global invariants and logging
+ *  steps of atomic counter test */
+void checkAtomicCounter(
+    int tid,
+    uint64_t step,
+    DeterministicAtomic<int>& shared,
+    AtomicCounterAux& aux) {
+  /* read shared data */
+  int val = shared.load_direct();
+  /* read auxiliary variables */
+  int sum = 0;
+  for (int v : aux.local_) {
+    sum += v;
+  }
+  /* log state */
+  VLOG(2) << "Step " << step << " -- tid " << tid << " -- shared counter "
+          << val << " -- sum increments " << sum;
+  /* check invariant */
+  if (val != sum) {
+    LOG(ERROR) << "Failed after step " << step;
+    LOG(ERROR) << "counter=(" << val << ") expected(" << sum << ")";
+    CHECK(false);
+  }
+}
+
+std::function<void(uint64_t, bool)> auxAtomicCounter(
+    DeterministicAtomic<int>& shared,
+    AtomicCounterAux& aux,
+    int tid) {
+  return [&shared, &aux, tid](uint64_t step, bool success) {
+    // update auxiliary data
+    if (success) {
+      aux.local_[tid]++;
+    }
+    // check invariants
+    checkAtomicCounter(tid, step, shared, aux);
+  };
+}
+
+DEFINE_bool(bug, false, "Introduce bug");
+DEFINE_int64(seed, 0, "Seed for random number generator");
+DEFINE_int32(num_threads, 2, "Number of threads");
+DEFINE_int32(num_iterations, 10, "Number of iterations");
+
+TEST(DSchedCustom, atomic_add) {
+  bool bug = FLAGS_bug;
+  long seed = FLAGS_seed;
+  int nthr = FLAGS_num_threads;
+  int niter = FLAGS_num_iterations;
+
+  CHECK_GT(nthr, 0);
+
+  DeterministicAtomic<int> counter{0};
+  AtomicCounterAux auxData(nthr);
+  DeterministicSchedule sched(DeterministicSchedule::uniform(seed));
+
+  std::vector<std::thread> threads(nthr);
+  for (int tid = 0; tid < nthr; ++tid) {
+    threads[tid] = DeterministicSchedule::thread([&, tid]() {
+      auto auxFn = auxAtomicCounter(counter, auxData, tid);
+      for (int i = 0; i < niter; ++i) {
+        if (bug && (tid == 0) && (i % 10 == 0)) {
+          int newval = counter.load() + 1;
+          DeterministicSchedule::setAux(auxFn);
+          counter.store(newval);
+        } else {
+          DeterministicSchedule::setAux(auxFn);
+          counter.fetch_add(1);
+        }
+      }
+    });
+  }
+  for (auto& t : threads) {
+    DeterministicSchedule::join(t);
+  }
+  EXPECT_EQ(counter.load_direct(), nthr * niter);
+}
+
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
   gflags::ParseCommandLineFlags(&argc, &argv, true);