Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Unified Diff: webkit/dom_storage/dom_storage_area.cc

Issue 9718029: DomStorage commit task sequencing. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webkit/dom_storage/dom_storage_area.h ('k') | webkit/dom_storage/dom_storage_area_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webkit/dom_storage/dom_storage_area.cc
===================================================================
--- webkit/dom_storage/dom_storage_area.cc (revision 127736)
+++ webkit/dom_storage/dom_storage_area.cc (working copy)
@@ -16,6 +16,14 @@
namespace dom_storage {
+static const int kCommitTimerSeconds = 1;
+
+DomStorageArea::CommitBatch::CommitBatch()
+ : clear_all_first(false) {
+}
+DomStorageArea::CommitBatch::~CommitBatch() {}
+
+
// static
const FilePath::CharType DomStorageArea::kDatabaseFileExtension[] =
FILE_PATH_LITERAL(".localstorage");
@@ -37,51 +45,35 @@
directory_(directory),
task_runner_(task_runner),
map_(new DomStorageMap(kPerAreaQuota)),
- backing_(NULL),
- initial_import_done_(false),
- clear_all_next_commit_(false),
- commit_in_flight_(false) {
-
+ is_initial_import_done_(true),
+ is_shutdown_(false) {
if (namespace_id == kLocalStorageNamespaceId && !directory.empty()) {
FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
backing_.reset(new DomStorageDatabase(path));
- } else {
- // Not a local storage area or no directory specified for backing
- // database, (i.e. it's an incognito profile).
- initial_import_done_ = true;
+ is_initial_import_done_ = false;
}
}
DomStorageArea::~DomStorageArea() {
- if (clear_all_next_commit_ || !changed_values_.empty()) {
- // Still some data left that was not committed to disk, try now.
- // We do this regardless of whether we think a commit is in flight
- // as there is no guarantee that that commit will actually get
- // processed. For example the task_runner_'s message loop could
- // unexpectedly quit before the delayed task is fired and leave the
- // commit_in_flight_ flag set. But there's no way for us to determine
- // that has happened so force a commit now.
-
- CommitChanges();
-
- // TODO(benm): It's possible that the commit failed, and in
- // that case we're going to lose data. Integrate with UMA
- // to gather stats about how often this actually happens,
- // so that we can figure out a contingency plan.
- }
}
unsigned DomStorageArea::Length() {
+ if (is_shutdown_)
+ return 0;
InitialImportIfNeeded();
return map_->Length();
}
NullableString16 DomStorageArea::Key(unsigned index) {
+ if (is_shutdown_)
+ return NullableString16(true);
InitialImportIfNeeded();
return map_->Key(index);
}
NullableString16 DomStorageArea::GetItem(const string16& key) {
+ if (is_shutdown_)
+ return NullableString16(true);
InitialImportIfNeeded();
return map_->GetItem(key);
}
@@ -89,31 +81,36 @@
bool DomStorageArea::SetItem(const string16& key,
const string16& value,
NullableString16* old_value) {
+ if (is_shutdown_)
+ return false;
InitialImportIfNeeded();
-
if (!map_->HasOneRef())
map_ = map_->DeepCopy();
bool success = map_->SetItem(key, value, old_value);
if (success && backing_.get()) {
- changed_values_[key] = NullableString16(value, false);
- ScheduleCommitChanges();
+ CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
+ commit_batch->changed_values[key] = NullableString16(value, false);
}
return success;
}
bool DomStorageArea::RemoveItem(const string16& key, string16* old_value) {
+ if (is_shutdown_)
+ return false;
InitialImportIfNeeded();
if (!map_->HasOneRef())
map_ = map_->DeepCopy();
bool success = map_->RemoveItem(key, old_value);
if (success && backing_.get()) {
- changed_values_[key] = NullableString16(true);
- ScheduleCommitChanges();
+ CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
+ commit_batch->changed_values[key] = NullableString16(true);
}
return success;
}
bool DomStorageArea::Clear() {
+ if (is_shutdown_)
+ return false;
InitialImportIfNeeded();
if (map_->Length() == 0)
return false;
@@ -121,9 +118,9 @@
map_ = new DomStorageMap(kPerAreaQuota);
if (backing_.get()) {
- changed_values_.clear();
- clear_all_next_commit_ = true;
- ScheduleCommitChanges();
+ CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
+ commit_batch->clear_all_first = true;
+ commit_batch->changed_values.clear();
}
return true;
@@ -132,17 +129,31 @@
DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) {
DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
- // SessionNamespaces aren't backed by files on disk.
- DCHECK(!backing_.get());
+ DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk.
DomStorageArea* copy = new DomStorageArea(destination_namespace_id, origin_,
FilePath(), task_runner_);
copy->map_ = map_;
+ copy->is_shutdown_ = is_shutdown_;
return copy;
}
+void DomStorageArea::Shutdown() {
+ DCHECK(!is_shutdown_);
+ is_shutdown_ = true;
+ map_ = NULL;
+ if (!backing_.get())
+ return;
+
+ bool success = task_runner_->PostShutdownBlockingTask(
+ FROM_HERE,
+ DomStorageTaskRunner::COMMIT_SEQUENCE,
+ base::Bind(&DomStorageArea::ShutdownInCommitSequence, this));
+ DCHECK(success);
+}
+
void DomStorageArea::InitialImportIfNeeded() {
- if (initial_import_done_)
+ if (is_initial_import_done_)
return;
DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
@@ -151,31 +162,85 @@
ValuesMap initial_values;
backing_->ReadAllValues(&initial_values);
map_->SwapValues(&initial_values);
- initial_import_done_ = true;
+ is_initial_import_done_ = true;
}
-void DomStorageArea::ScheduleCommitChanges() {
+DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() {
+ DCHECK(!is_shutdown_);
+ if (!commit_batch_.get()) {
+ commit_batch_.reset(new CommitBatch());
+
+ // Start a timer to commit any changes that accrue in the batch,
+ // but only if a commit is not currently in flight. In that case
+ // the timer will be started after the current commit has happened.
+ if (!in_flight_commit_batch_.get()) {
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DomStorageArea::OnCommitTimer, this),
+ base::TimeDelta::FromSeconds(kCommitTimerSeconds));
+ }
+ }
+ return commit_batch_.get();
+}
+
+void DomStorageArea::OnCommitTimer() {
DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
+ if (is_shutdown_)
+ return;
+
DCHECK(backing_.get());
- DCHECK(clear_all_next_commit_ || !changed_values_.empty());
- DCHECK(task_runner_.get());
+ DCHECK(commit_batch_.get());
+ DCHECK(!in_flight_commit_batch_.get());
- if (commit_in_flight_)
- return;
+ // This method executes on the primary sequence, we schedule
+ // a task for immediate execution on the commit sequence.
+ in_flight_commit_batch_ = commit_batch_.Pass();
+ bool success = task_runner_->PostShutdownBlockingTask(
+ FROM_HERE,
+ DomStorageTaskRunner::COMMIT_SEQUENCE,
+ base::Bind(&DomStorageArea::CommitChanges, this));
+ DCHECK(success);
+}
- commit_in_flight_ = task_runner_->PostDelayedTask(
- FROM_HERE, base::Bind(&DomStorageArea::CommitChanges, this),
- base::TimeDelta::FromSeconds(1));
- DCHECK(commit_in_flight_);
+void DomStorageArea::CommitChanges() {
+ // This method executes on the commit sequence.
+ DCHECK(in_flight_commit_batch_.get());
+ bool success = backing_->CommitChanges(
+ in_flight_commit_batch_->clear_all_first,
+ in_flight_commit_batch_->changed_values);
+ DCHECK(success); // TODO(michaeln): what if it fails?
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DomStorageArea::OnCommitComplete, this));
}
-void DomStorageArea::CommitChanges() {
+void DomStorageArea::OnCommitComplete() {
+ // We're back on the primary sequence in this method.
+ if (is_shutdown_)
+ return;
+ in_flight_commit_batch_.reset();
+ if (commit_batch_.get()) {
+ // More changes have accrued, restart the timer.
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DomStorageArea::OnCommitTimer, this),
+ base::TimeDelta::FromSeconds(kCommitTimerSeconds));
+ }
+}
+
+void DomStorageArea::ShutdownInCommitSequence() {
+ // This method executes on the commit sequence.
DCHECK(backing_.get());
- if (backing_->CommitChanges(clear_all_next_commit_, changed_values_)) {
- clear_all_next_commit_ = false;
- changed_values_.clear();
+ if (commit_batch_.get()) {
+ // Commit any changes that accrued prior to the timer firing.
+ bool success = backing_->CommitChanges(
+ commit_batch_->clear_all_first,
+ commit_batch_->changed_values);
+ DCHECK(success);
}
- commit_in_flight_ = false;
+ commit_batch_.reset();
+ in_flight_commit_batch_.reset();
+ backing_.reset();
}
} // namespace dom_storage
« no previous file with comments | « webkit/dom_storage/dom_storage_area.h ('k') | webkit/dom_storage/dom_storage_area_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698