allow run-once callbacks
authorIgor Zinkovsky <igorzi@fb.com>
Tue, 13 Dec 2016 07:21:55 +0000 (23:21 -0800)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Tue, 13 Dec 2016 07:33:00 +0000 (23:33 -0800)
Summary: Adding new `addFunctionOnce` method that executes provided callback only once.

Reviewed By: yfeldblum

Differential Revision: D4315635

fbshipit-source-id: 4819ec30b4f2e4ab3185a37158404e1c7a96758a

folly/experimental/FunctionScheduler.cpp
folly/experimental/FunctionScheduler.h
folly/experimental/test/FunctionSchedulerTest.cpp

index 2e0ee6ebea9c78f24d0b908548f09e50ecd900f1..98bd9ed71e85c8d3b2b0bbbe30d82b3c4db49649 100644 (file)
@@ -96,12 +96,13 @@ void FunctionScheduler::addFunction(Function<void()>&& cb,
                                     milliseconds interval,
                                     StringPiece nameID,
                                     milliseconds startDelay) {
-  addFunctionGenericDistribution(
+  addFunctionInternal(
       std::move(cb),
       ConstIntervalFunctor(interval),
       nameID.str(),
       to<std::string>(interval.count(), "ms"),
-      startDelay);
+      startDelay,
+      false /*runOnce*/);
 }
 
 void FunctionScheduler::addFunction(Function<void()>&& cb,
@@ -110,30 +111,45 @@ void FunctionScheduler::addFunction(Function<void()>&& cb,
                                     StringPiece nameID,
                                     milliseconds startDelay) {
   if (latencyDistr.isPoisson) {
-    addFunctionGenericDistribution(
+    addFunctionInternal(
         std::move(cb),
         PoissonDistributionFunctor(latencyDistr.poissonMean),
         nameID.str(),
         to<std::string>(latencyDistr.poissonMean, "ms (Poisson mean)"),
-        startDelay);
+        startDelay,
+        false /*runOnce*/);
   } else {
     addFunction(std::move(cb), interval, nameID, startDelay);
   }
 }
 
+void FunctionScheduler::addFunctionOnce(
+    Function<void()>&& cb,
+    StringPiece nameID,
+    milliseconds startDelay) {
+  addFunctionInternal(
+      std::move(cb),
+      ConstIntervalFunctor(milliseconds::zero()),
+      nameID.str(),
+      "once",
+      startDelay,
+      true /*runOnce*/);
+}
+
 void FunctionScheduler::addFunctionUniformDistribution(
     Function<void()>&& cb,
     milliseconds minInterval,
     milliseconds maxInterval,
     StringPiece nameID,
     milliseconds startDelay) {
-  addFunctionGenericDistribution(
+  addFunctionInternal(
       std::move(cb),
       UniformDistributionFunctor(minInterval, maxInterval),
       nameID.str(),
       to<std::string>(
           "[", minInterval.count(), " , ", maxInterval.count(), "] ms"),
-      startDelay);
+      startDelay,
+      false /*runOnce*/);
 }
 
 void FunctionScheduler::addFunctionGenericDistribution(
@@ -142,6 +158,22 @@ void FunctionScheduler::addFunctionGenericDistribution(
     const std::string& nameID,
     const std::string& intervalDescr,
     milliseconds startDelay) {
+  addFunctionInternal(
+      std::move(cb),
+      std::move(intervalFunc),
+      nameID,
+      intervalDescr,
+      startDelay,
+      false /*runOnce*/);
+}
+
+void FunctionScheduler::addFunctionInternal(
+    Function<void()>&& cb,
+    IntervalDistributionFunc&& intervalFunc,
+    const std::string& nameID,
+    const std::string& intervalDescr,
+    milliseconds startDelay,
+    bool runOnce) {
   if (!cb) {
     throw std::invalid_argument(
         "FunctionScheduler: Scheduled function must be set");
@@ -177,7 +209,8 @@ void FunctionScheduler::addFunctionGenericDistribution(
           std::move(intervalFunc),
           nameID,
           intervalDescr,
-          startDelay));
+          startDelay,
+          runOnce));
 }
 
 bool FunctionScheduler::cancelFunction(StringPiece nameID) {
@@ -382,6 +415,10 @@ void FunctionScheduler::runOneFunction(std::unique_lock<std::mutex>& lock,
     // We shouldn't reschedule it;
     return;
   }
+  if (currentFunction_->runOnce) {
+    // Don't reschedule if the function only needed to run once.
+    return;
+  }
   // Clear currentFunction_
   CHECK_EQ(currentFunction_, &func);
   currentFunction_ = nullptr;
index 1668c501e6aef35d6086b51660aa25257c7aaa70..f3a2d364a6596e92966506c3ee64b5ac0295defb 100644 (file)
@@ -110,6 +110,14 @@ class FunctionScheduler {
       StringPiece nameID = StringPiece(),
       std::chrono::milliseconds startDelay = std::chrono::milliseconds(0));
 
+  /**
+   * Adds a new function to the FunctionScheduler to run only once.
+   */
+  void addFunctionOnce(
+      Function<void()>&& cb,
+      StringPiece nameID = StringPiece(),
+      std::chrono::milliseconds startDelay = std::chrono::milliseconds(0));
+
   /**
     * Add a new function to the FunctionScheduler with the time
     * interval being distributed uniformly within the given interval
@@ -194,18 +202,22 @@ class FunctionScheduler {
     std::string name;
     std::chrono::milliseconds startDelay;
     std::string intervalDescr;
+    bool runOnce;
 
-    RepeatFunc(Function<void()>&& cback,
-               IntervalDistributionFunc&& intervalFn,
-               const std::string& nameID,
-               const std::string& intervalDistDescription,
-               std::chrono::milliseconds delay)
+    RepeatFunc(
+        Function<void()>&& cback,
+        IntervalDistributionFunc&& intervalFn,
+        const std::string& nameID,
+        const std::string& intervalDistDescription,
+        std::chrono::milliseconds delay,
+        bool once)
         : cb(std::move(cback)),
           intervalFunc(std::move(intervalFn)),
           nextRunTime(),
           name(nameID),
           startDelay(delay),
-          intervalDescr(intervalDistDescription) {}
+          intervalDescr(intervalDistDescription),
+          runOnce(once) {}
 
     std::chrono::steady_clock::time_point getNextRunTime() const {
       return nextRunTime;
@@ -240,6 +252,14 @@ class FunctionScheduler {
   void addFunctionToHeap(const std::unique_lock<std::mutex>& lock,
                          RepeatFunc&& func);
 
+  void addFunctionInternal(
+      Function<void()>&& cb,
+      IntervalDistributionFunc&& intervalFunc,
+      const std::string& nameID,
+      const std::string& intervalDescr,
+      std::chrono::milliseconds startDelay,
+      bool runOnce);
+
   std::thread thread_;
 
   // Mutex to protect our member variables.
index eb122d6f10f9eb83c9dee5de1dd809e8bdb7e303..2de87ed36ab1ebd22ada03c68e6368b59c3fda98 100644 (file)
@@ -446,3 +446,15 @@ TEST(FunctionScheduler, GammaIntervalDistribution) {
   delay(2);
   EXPECT_EQ(6, total);
 }
+
+TEST(FunctionScheduler, AddWithRunOnce) {
+  int total = 0;
+  FunctionScheduler fs;
+  fs.addFunctionOnce([&] { total += 2; }, "add2");
+  fs.start();
+  delay(1);
+  EXPECT_EQ(2, total);
+  delay(2);
+  EXPECT_EQ(2, total);
+  fs.shutdown();
+}