Add V8 platform API to call delayed task.
authorulan <ulan@chromium.org>
Wed, 17 Jun 2015 12:09:34 +0000 (05:09 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 17 Jun 2015 12:09:40 +0000 (12:09 +0000)
Delayed tasks can be used to perform non-urgent clean up work.

BUG=chromium:490559
LOG=NO

Review URL: https://codereview.chromium.org/1179153002

Cr-Commit-Position: refs/heads/master@{#29084}

include/v8-platform.h
src/libplatform/default-platform.cc
src/libplatform/default-platform.h
test/unittests/libplatform/default-platform-unittest.cc

index 67fb384c99ba523783a110ba0f9b077da6c5ff09..be9e5c0c6b46ed122de88253e96f0cbaf8a0d98a 100644 (file)
@@ -56,6 +56,17 @@ class Platform {
    */
   virtual void CallOnForegroundThread(Isolate* isolate, Task* task) = 0;
 
+  /**
+   * Schedules a task to be invoked on a foreground thread wrt a specific
+   * |isolate| after the given number of seconds |delay_in_seconds|.
+   * Tasks posted for the same isolate should be execute in order of
+   * scheduling. The definition of "foreground" is opaque to V8.
+   */
+  virtual void CallDelayedOnForegroundThread(Isolate* isolate, Task* task,
+                                             double delay_in_seconds) {
+    // TODO(ulan): Make this function abstract after V8 roll in Chromium.
+  }
+
   /**
    * Monotonically increasing time in seconds from an arbitrary fixed point in
    * the past. This function is expected to return at least
index 1ac52f919f3f4889037512e332a5b07fba25146c..2885d55de7066c19546cee6a82587ffa3b050ad9 100644 (file)
@@ -78,23 +78,56 @@ void DefaultPlatform::EnsureInitialized() {
 }
 
 
+Task* DefaultPlatform::PopTaskInMainThreadQueue(v8::Isolate* isolate) {
+  auto it = main_thread_queue_.find(isolate);
+  if (it == main_thread_queue_.end() || it->second.empty()) {
+    return NULL;
+  }
+  Task* task = it->second.front();
+  it->second.pop();
+  return task;
+}
+
+
+Task* DefaultPlatform::PopTaskInMainThreadDelayedQueue(v8::Isolate* isolate) {
+  auto it = main_thread_delayed_queue_.find(isolate);
+  if (it == main_thread_delayed_queue_.end() || it->second.empty()) {
+    return NULL;
+  }
+  double now = MonotonicallyIncreasingTime();
+  std::pair<double, Task*> deadline_and_task = it->second.top();
+  if (deadline_and_task.first > now) {
+    return NULL;
+  }
+  it->second.pop();
+  return deadline_and_task.second;
+}
+
+
 bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate) {
   Task* task = NULL;
   {
     base::LockGuard<base::Mutex> guard(&lock_);
-    std::map<v8::Isolate*, std::queue<Task*> >::iterator it =
-        main_thread_queue_.find(isolate);
-    if (it == main_thread_queue_.end() || it->second.empty()) {
+
+    // Move delayed tasks that hit their deadline to the main queue.
+    task = PopTaskInMainThreadDelayedQueue(isolate);
+    while (task != NULL) {
+      main_thread_queue_[isolate].push(task);
+      task = PopTaskInMainThreadDelayedQueue(isolate);
+    }
+
+    task = PopTaskInMainThreadQueue(isolate);
+
+    if (task == NULL) {
       return false;
     }
-    task = it->second.front();
-    it->second.pop();
   }
   task->Run();
   delete task;
   return true;
 }
 
+
 void DefaultPlatform::CallOnBackgroundThread(Task *task,
                                              ExpectedRuntime expected_runtime) {
   EnsureInitialized();
@@ -108,6 +141,14 @@ void DefaultPlatform::CallOnForegroundThread(v8::Isolate* isolate, Task* task) {
 }
 
 
+void DefaultPlatform::CallDelayedOnForegroundThread(Isolate* isolate,
+                                                    Task* task,
+                                                    double delay_in_seconds) {
+  double deadline = MonotonicallyIncreasingTime() + delay_in_seconds;
+  main_thread_delayed_queue_[isolate].push(std::make_pair(deadline, task));
+}
+
+
 double DefaultPlatform::MonotonicallyIncreasingTime() {
   return base::TimeTicks::HighResolutionNow().ToInternalValue() /
          static_cast<double>(base::Time::kMicrosecondsPerSecond);
index 72b4d91aa8cfebbce16c71fb5cff23c128785746..fba5803f406054647b3d376527c9f9136b2a3765 100644 (file)
@@ -5,6 +5,7 @@
 #ifndef V8_LIBPLATFORM_DEFAULT_PLATFORM_H_
 #define V8_LIBPLATFORM_DEFAULT_PLATFORM_H_
 
+#include <functional>
 #include <map>
 #include <queue>
 #include <vector>
@@ -37,11 +38,16 @@ class DefaultPlatform : public Platform {
       Task* task, ExpectedRuntime expected_runtime) override;
   virtual void CallOnForegroundThread(v8::Isolate* isolate,
                                       Task* task) override;
+  virtual void CallDelayedOnForegroundThread(Isolate* isolate, Task* task,
+                                             double delay_in_seconds) override;
   double MonotonicallyIncreasingTime() override;
 
  private:
   static const int kMaxThreadPoolSize;
 
+  Task* PopTaskInMainThreadQueue(v8::Isolate* isolate);
+  Task* PopTaskInMainThreadDelayedQueue(v8::Isolate* isolate);
+
   base::Mutex lock_;
   bool initialized_;
   int thread_pool_size_;
@@ -49,6 +55,12 @@ class DefaultPlatform : public Platform {
   TaskQueue queue_;
   std::map<v8::Isolate*, std::queue<Task*> > main_thread_queue_;
 
+  typedef std::pair<double, Task*> DelayedEntry;
+  std::map<v8::Isolate*,
+           std::priority_queue<DelayedEntry, std::vector<DelayedEntry>,
+                               std::greater<DelayedEntry> > >
+      main_thread_delayed_queue_;
+
   DISALLOW_COPY_AND_ASSIGN(DefaultPlatform);
 };
 
index d2c160e5587557c72c5c138e4a826784aaa4cdb9..ffa3199444eb4457017921bf9e622bf0a1c8ce70 100644 (file)
@@ -19,6 +19,17 @@ struct MockTask : public Task {
   MOCK_METHOD0(Die, void());
 };
 
+
+class DefaultPlatformWithMockTime : public DefaultPlatform {
+ public:
+  DefaultPlatformWithMockTime() : time_(0) {}
+  double MonotonicallyIncreasingTime() override { return time_; }
+  void IncreaseTime(double seconds) { time_ += seconds; }
+
+ private:
+  double time_;
+};
+
 }  // namespace
 
 
@@ -39,5 +50,66 @@ TEST(DefaultPlatformTest, PumpMessageLoop) {
   EXPECT_FALSE(platform.PumpMessageLoop(isolate));
 }
 
+
+TEST(DefaultPlatformTest, PumpMessageLoopDelayed) {
+  InSequence s;
+
+  int dummy;
+  Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
+
+  DefaultPlatformWithMockTime platform;
+  EXPECT_FALSE(platform.PumpMessageLoop(isolate));
+
+  StrictMock<MockTask>* task1 = new StrictMock<MockTask>;
+  StrictMock<MockTask>* task2 = new StrictMock<MockTask>;
+  platform.CallDelayedOnForegroundThread(isolate, task2, 100);
+  platform.CallDelayedOnForegroundThread(isolate, task1, 10);
+
+  EXPECT_FALSE(platform.PumpMessageLoop(isolate));
+
+  platform.IncreaseTime(11);
+  EXPECT_CALL(*task1, Run());
+  EXPECT_CALL(*task1, Die());
+  EXPECT_TRUE(platform.PumpMessageLoop(isolate));
+
+  EXPECT_FALSE(platform.PumpMessageLoop(isolate));
+
+  platform.IncreaseTime(90);
+  EXPECT_CALL(*task2, Run());
+  EXPECT_CALL(*task2, Die());
+  EXPECT_TRUE(platform.PumpMessageLoop(isolate));
+}
+
+
+TEST(DefaultPlatformTest, PumpMessageLoopNoStarvation) {
+  InSequence s;
+
+  int dummy;
+  Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
+
+  DefaultPlatformWithMockTime platform;
+  EXPECT_FALSE(platform.PumpMessageLoop(isolate));
+
+  StrictMock<MockTask>* task1 = new StrictMock<MockTask>;
+  StrictMock<MockTask>* task2 = new StrictMock<MockTask>;
+  StrictMock<MockTask>* task3 = new StrictMock<MockTask>;
+  platform.CallOnForegroundThread(isolate, task1);
+  platform.CallDelayedOnForegroundThread(isolate, task2, 10);
+  platform.IncreaseTime(11);
+
+  EXPECT_CALL(*task1, Run());
+  EXPECT_CALL(*task1, Die());
+  EXPECT_TRUE(platform.PumpMessageLoop(isolate));
+
+  platform.CallOnForegroundThread(isolate, task3);
+
+  EXPECT_CALL(*task2, Run());
+  EXPECT_CALL(*task2, Die());
+  EXPECT_TRUE(platform.PumpMessageLoop(isolate));
+  EXPECT_CALL(*task3, Run());
+  EXPECT_CALL(*task3, Die());
+  EXPECT_TRUE(platform.PumpMessageLoop(isolate));
+}
+
 }  // namespace platform
 }  // namespace v8