blob: 932932f9d7d3d6cbaa3e74df8b230028a535cdb7 [file] [log] [blame]
// Copyright 2012 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// An implementation of the Scheduler interface for unit testing (in a
// single-threaded environment).
#ifndef GOOGLE_CACHEINVALIDATION_TEST_DETERMINISTIC_SCHEDULER_H_
#define GOOGLE_CACHEINVALIDATION_TEST_DETERMINISTIC_SCHEDULER_H_
#include <queue>
#include <string>
#include <utility>
#include "google/cacheinvalidation/include/system-resources.h"
#include "google/cacheinvalidation/deps/callback.h"
#include "google/cacheinvalidation/deps/logging.h"
#include "google/cacheinvalidation/deps/string_util.h"
#include "google/cacheinvalidation/deps/time.h"
#include "google/cacheinvalidation/impl/log-macro.h"
#include "google/cacheinvalidation/impl/run-state.h"
namespace invalidation {
// An entry in the work queue. Ensures that tasks don't run until their
// scheduled time, and for a given time, they run in the order in which they
// were enqueued.
struct TaskEntry {
TaskEntry(Time time, int64 id, Closure* task)
: time(time), id(id), task(task) {}
bool operator<(const TaskEntry& other) const {
// Priority queue returns *largest* element first.
return (time > other.time) ||
((time == other.time) && (id > other.id));
}
Time time; // the time at which to run
int64 id; // the order in which this task was enqueued
Closure* task; // the task to be run
};
class DeterministicScheduler : public Scheduler {
public:
// Caller retains ownershup of |logger|.
explicit DeterministicScheduler(Logger* logger)
: current_id_(0), running_internal_(false), logger_(logger) {}
virtual ~DeterministicScheduler() {
StopScheduler();
}
virtual void SetSystemResources(SystemResources* resources) {
// Nothing to do.
}
virtual Time GetCurrentTime() const {
return current_time_;
}
void StartScheduler() {
run_state_.Start();
}
void StopScheduler();
virtual void Schedule(TimeDelta delay, Closure* task);
virtual bool IsRunningOnThread() const {
return running_internal_;
}
void SetInitialTime(Time new_time) {
current_time_ = new_time;
}
// Passes |delta_time| in increments of at most |step|, executing all pending
// tasks during that interval.
void PassTime(TimeDelta delta_time, TimeDelta step);
// Passes |delta_time| in default-sized increments, executing all pending
// tasks.
void PassTime(TimeDelta delta_time) {
PassTime(delta_time, DefaultTimeStep());
}
private:
// Runs all the work in the queue that should be executed by the current time.
// Note that tasks run may enqueue additional immediate tasks, and this call
// won't return until they've completed as well. While these tasks are
// running, the running_internal_ flag is set, so IsRunningOnThread()
// will return true.
void RunReadyTasks();
// Default time step when simulating passage of time. Chosen to be
// significantly smaller than any scheduling interval used by the client
// library.
static TimeDelta DefaultTimeStep() {
return TimeDelta::FromMilliseconds(10);
}
void ModifyTime(TimeDelta delta_time) {
current_time_ += delta_time;
}
// Attempts to run a task, returning true is there was a task to run.
bool RunNextTask();
// The current time, which may be set by the test.
Time current_time_;
// The id number of the next task.
uint64 current_id_;
// Whether or not the scheduler has been started/stopped.
RunState run_state_;
// Whether or not we're currently running internal tasks from the internal
// queue.
bool running_internal_;
// A priority queue on which the actual tasks are enqueued.
std::priority_queue<TaskEntry> work_queue_;
// A logger.
Logger* logger_;
};
// A simple deterministic scheduler that always indicates that it is on
// the correct thread.
class SimpleDeterministicScheduler : public DeterministicScheduler {
public:
explicit SimpleDeterministicScheduler(Logger* logger)
: DeterministicScheduler(logger) {}
virtual bool IsRunningOnThread() const {
return true;
}
};
} // namespace invalidation
#endif // GOOGLE_CACHEINVALIDATION_TEST_DETERMINISTIC_SCHEDULER_H_