blob: 1e25a40b0ea9701975d962ba643300d235613e36 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PREDICTORS_GLOWPLUG_KEY_VALUE_DATA_H_
#define CHROME_BROWSER_PREDICTORS_GLOWPLUG_KEY_VALUE_DATA_H_
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/timer/timer.h"
#include "chrome/browser/predictors/glowplug_key_value_table.h"
#include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
#include "content/public/browser/browser_thread.h"
class PredictorsHandler;
namespace predictors {
// The class provides a synchronous access to the data backed by
// GlowplugKeyValueTable<T>. The current implementation caches all the data in
// the memory. The cache size is limited by max_size parameter using Compare
// function to decide which entry should be evicted.
//
// InitializeOnDBSequence() must be called on the DB sequence of the
// ResourcePrefetchPredictorTables. All other methods must be called on UI
// thread.
template <typename T, typename Compare>
class GlowplugKeyValueData {
public:
GlowplugKeyValueData(scoped_refptr<ResourcePrefetchPredictorTables> tables,
GlowplugKeyValueTable<T>* backend,
size_t max_size,
base::TimeDelta flush_delay);
// Must be called on the DB sequence of the ResourcePrefetchPredictorTables
// before calling all other methods.
void InitializeOnDBSequence();
// Assigns data associated with the |key| to |data|. Returns true iff the
// |key| exists, false otherwise. |data| pointer may be nullptr to get the
// return value only.
bool TryGetData(const std::string& key, T* data) const;
// Assigns data associated with the |key| to |data|.
void UpdateData(const std::string& key, const T& data);
// Deletes data associated with the |keys| from the database.
void DeleteData(const std::vector<std::string>& keys);
// Deletes all entries from the database.
void DeleteAllData();
private:
friend class ResourcePrefetchPredictorTest;
friend class ::PredictorsHandler;
struct EntryCompare : private Compare {
bool operator()(const std::pair<std::string, T>& lhs,
const std::pair<std::string, T>& rhs) {
return Compare::operator()(lhs.second, rhs.second);
}
};
enum class DeferredOperation { kUpdate, kDelete };
void FlushDataToDisk();
scoped_refptr<ResourcePrefetchPredictorTables> tables_;
GlowplugKeyValueTable<T>* backend_table_;
std::unique_ptr<std::map<std::string, T>> data_cache_;
std::unordered_map<std::string, DeferredOperation> deferred_updates_;
base::RepeatingTimer flush_timer_;
const base::TimeDelta flush_delay_;
const size_t max_size_;
EntryCompare entry_compare_;
DISALLOW_COPY_AND_ASSIGN(GlowplugKeyValueData);
};
template <typename T, typename Compare>
GlowplugKeyValueData<T, Compare>::GlowplugKeyValueData(
scoped_refptr<ResourcePrefetchPredictorTables> tables,
GlowplugKeyValueTable<T>* backend,
size_t max_size,
base::TimeDelta flush_delay)
: tables_(tables),
backend_table_(backend),
flush_delay_(flush_delay),
max_size_(max_size) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
template <typename T, typename Compare>
void GlowplugKeyValueData<T, Compare>::InitializeOnDBSequence() {
DCHECK(tables_->GetTaskRunner()->RunsTasksInCurrentSequence());
auto data_map = std::make_unique<std::map<std::string, T>>();
tables_->ExecuteDBTaskOnDBSequence(
base::BindOnce(&GlowplugKeyValueTable<T>::GetAllData,
base::Unretained(backend_table_), data_map.get()));
// To ensure invariant that data_cache_.size() <= max_size_.
std::vector<std::string> keys_to_delete;
while (data_map->size() > max_size_) {
auto entry_to_delete =
std::min_element(data_map->begin(), data_map->end(), entry_compare_);
keys_to_delete.emplace_back(entry_to_delete->first);
data_map->erase(entry_to_delete);
}
if (keys_to_delete.size() > 0) {
tables_->ExecuteDBTaskOnDBSequence(base::BindOnce(
&GlowplugKeyValueTable<T>::DeleteData, base::Unretained(backend_table_),
std::vector<std::string>(keys_to_delete)));
}
data_cache_ = std::move(data_map);
}
template <typename T, typename Compare>
bool GlowplugKeyValueData<T, Compare>::TryGetData(const std::string& key,
T* data) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(data_cache_);
auto it = data_cache_->find(key);
if (it == data_cache_->end())
return false;
if (data)
*data = it->second;
return true;
}
template <typename T, typename Compare>
void GlowplugKeyValueData<T, Compare>::UpdateData(const std::string& key,
const T& data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(data_cache_);
auto it = data_cache_->find(key);
if (it == data_cache_->end()) {
if (data_cache_->size() == max_size_) {
auto entry_to_delete = std::min_element(
data_cache_->begin(), data_cache_->end(), entry_compare_);
deferred_updates_[entry_to_delete->first] = DeferredOperation::kDelete;
data_cache_->erase(entry_to_delete);
}
data_cache_->emplace(key, data);
} else {
it->second = data;
}
deferred_updates_[key] = DeferredOperation::kUpdate;
if (flush_delay_.is_zero()) {
// Flush immediately, only for tests.
FlushDataToDisk();
} else if (!flush_timer_.IsRunning()) {
flush_timer_.Start(FROM_HERE, flush_delay_, this,
&GlowplugKeyValueData::FlushDataToDisk);
}
}
template <typename T, typename Compare>
void GlowplugKeyValueData<T, Compare>::DeleteData(
const std::vector<std::string>& keys) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(data_cache_);
for (const std::string& key : keys) {
if (data_cache_->erase(key))
deferred_updates_[key] = DeferredOperation::kDelete;
}
// Run all deferred updates immediately because it was requested by user.
if (!deferred_updates_.empty())
FlushDataToDisk();
}
template <typename T, typename Compare>
void GlowplugKeyValueData<T, Compare>::DeleteAllData() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(data_cache_);
data_cache_->clear();
deferred_updates_.clear();
// Delete all the content of the database immediately because it was requested
// by user.
tables_->ScheduleDBTask(
FROM_HERE, base::BindOnce(&GlowplugKeyValueTable<T>::DeleteAllData,
base::Unretained(backend_table_)));
}
template <typename T, typename Compare>
void GlowplugKeyValueData<T, Compare>::FlushDataToDisk() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (deferred_updates_.empty())
return;
std::vector<std::string> keys_to_delete;
for (const auto& entry : deferred_updates_) {
const std::string& key = entry.first;
switch (entry.second) {
case DeferredOperation::kUpdate: {
auto it = data_cache_->find(key);
if (it != data_cache_->end()) {
tables_->ScheduleDBTask(
FROM_HERE, base::BindOnce(&GlowplugKeyValueTable<T>::UpdateData,
base::Unretained(backend_table_), key,
it->second));
}
break;
}
case DeferredOperation::kDelete:
keys_to_delete.push_back(key);
}
}
if (!keys_to_delete.empty()) {
tables_->ScheduleDBTask(
FROM_HERE,
base::BindOnce(&GlowplugKeyValueTable<T>::DeleteData,
base::Unretained(backend_table_), keys_to_delete));
}
deferred_updates_.clear();
}
} // namespace predictors
#endif // CHROME_BROWSER_PREDICTORS_GLOWPLUG_KEY_VALUE_DATA_H_