Commit 31e3177b authored by ulan's avatar ulan Committed by Commit bot

Add V8 platform API to call delayed task.

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}
parent 885455e9
......@@ -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
......
......@@ -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);
......
......@@ -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);
};
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment