sql_store_base.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/offline_pages/task/sql_store_base.h"
  5. #include <iterator>
  6. #include <utility>
  7. #include "base/files/file_path.h"
  8. #include "base/files/file_util.h"
  9. #include "base/logging.h"
  10. #include "base/task/sequenced_task_runner.h"
  11. #include "base/threading/thread_task_runner_handle.h"
  12. #include "base/trace_event/trace_event.h"
  13. namespace offline_pages {
  14. namespace {
  15. bool PrepareDirectory(const base::FilePath& path) {
  16. base::File::Error error = base::File::FILE_OK;
  17. if (!base::DirectoryExists(path.DirName())) {
  18. if (!base::CreateDirectoryAndGetError(path.DirName(), &error)) {
  19. DLOG(ERROR) << "Failed to create prefetch db directory: "
  20. << base::File::ErrorToString(error);
  21. return false;
  22. }
  23. }
  24. return true;
  25. }
  26. // TODO(fgorski): This function and this part of the system in general could
  27. // benefit from a better status code reportable through UMA to better capture
  28. // the reason for failure, aiding the process of repeated attempts to
  29. // open/initialize the database.
  30. bool InitializeSync(
  31. sql::Database* db,
  32. const base::FilePath& path,
  33. const std::string& histogram_tag,
  34. base::OnceCallback<bool(sql::Database*)> initialize_schema) {
  35. db->set_histogram_tag(histogram_tag);
  36. const bool in_memory = path.empty();
  37. if (!in_memory && !PrepareDirectory(path))
  38. return false;
  39. bool open_db_result = false;
  40. if (in_memory)
  41. open_db_result = db->OpenInMemory();
  42. else
  43. open_db_result = db->Open(path);
  44. if (!open_db_result) {
  45. DLOG(ERROR) << "Failed to open database, in memory: " << in_memory;
  46. return false;
  47. }
  48. db->Preload();
  49. return std::move(initialize_schema).Run(db);
  50. }
  51. void CloseDatabaseSync(
  52. sql::Database* db,
  53. scoped_refptr<base::SingleThreadTaskRunner> callback_runner,
  54. base::OnceClosure callback) {
  55. if (db)
  56. db->Close();
  57. callback_runner->PostTask(FROM_HERE, std::move(callback));
  58. }
  59. } // namespace
  60. // static
  61. constexpr base::TimeDelta SqlStoreBase::kClosingDelay;
  62. SqlStoreBase::SqlStoreBase(
  63. const std::string& histogram_tag,
  64. scoped_refptr<base::SequencedTaskRunner> background_task_runner,
  65. const base::FilePath& file_path)
  66. : background_task_runner_(background_task_runner),
  67. histogram_tag_(histogram_tag),
  68. db_file_path_(file_path),
  69. db_(nullptr, base::OnTaskRunnerDeleter(background_task_runner_)) {}
  70. SqlStoreBase::~SqlStoreBase() = default;
  71. void SqlStoreBase::SetInitializationStatusForTesting(
  72. InitializationStatus initialization_status,
  73. bool reset_db) {
  74. initialization_status_ = initialization_status;
  75. if (reset_db)
  76. db_.reset(nullptr);
  77. }
  78. void SqlStoreBase::Initialize(base::OnceClosure pending_command) {
  79. OnOpenStart(last_closing_time_);
  80. DCHECK_EQ(initialization_status_, InitializationStatus::kNotInitialized);
  81. initialization_status_ = InitializationStatus::kInProgress;
  82. // This is how we reset a pointer and provide deleter. This is necessary to
  83. // ensure that we can close the store more than once.
  84. db_ = DatabaseUniquePtr(new sql::Database({// These values are default.
  85. .exclusive_locking = true,
  86. .page_size = 4096,
  87. .cache_size = 500}),
  88. base::OnTaskRunnerDeleter(background_task_runner_));
  89. base::PostTaskAndReplyWithResult(
  90. background_task_runner_.get(), FROM_HERE,
  91. base::BindOnce(&InitializeSync, db_.get(), db_file_path_, histogram_tag_,
  92. GetSchemaInitializationFunction()),
  93. base::BindOnce(&SqlStoreBase::InitializeDone,
  94. weak_ptr_factory_.GetWeakPtr(),
  95. std::move(pending_command)));
  96. }
  97. void SqlStoreBase::InitializeDone(base::OnceClosure pending_command,
  98. bool success) {
  99. DCHECK_EQ(initialization_status_, InitializationStatus::kInProgress);
  100. if (success) {
  101. initialization_status_ = InitializationStatus::kSuccess;
  102. } else {
  103. initialization_status_ = InitializationStatus::kFailure;
  104. db_.reset();
  105. }
  106. CHECK(!pending_command.is_null());
  107. std::move(pending_command).Run();
  108. for (auto command_iter = std::make_move_iterator(pending_commands_.begin());
  109. command_iter != std::make_move_iterator(pending_commands_.end());
  110. ++command_iter) {
  111. (*command_iter).Run();
  112. }
  113. pending_commands_.clear();
  114. // Once pending commands are empty, we get back to kNotInitialized state, to
  115. // make it possible to retry initialization next time a DB operation is
  116. // attempted.
  117. if (initialization_status_ == InitializationStatus::kFailure)
  118. initialization_status_ = InitializationStatus::kNotInitialized;
  119. OnOpenDone(success);
  120. }
  121. void SqlStoreBase::ExecuteInternal(base::OnceClosure command) {
  122. if (initialization_status_ == InitializationStatus::kInProgress) {
  123. pending_commands_.push_back(std::move(command));
  124. return;
  125. }
  126. if (initialization_status_ == InitializationStatus::kNotInitialized) {
  127. Initialize(std::move(command));
  128. return;
  129. }
  130. std::move(command).Run();
  131. }
  132. sql::Database* SqlStoreBase::ExecuteBegin() {
  133. OnTaskBegin(initialization_status_ == InitializationStatus::kSuccess);
  134. // Ensure that any scheduled close operations are canceled.
  135. closing_weak_ptr_factory_.InvalidateWeakPtrs();
  136. return initialization_status_ == InitializationStatus::kSuccess ? db_.get()
  137. : nullptr;
  138. }
  139. void SqlStoreBase::CloseInternal() {
  140. OnCloseStart(initialization_status_);
  141. last_closing_time_ = base::TimeTicks::Now();
  142. initialization_status_ = InitializationStatus::kNotInitialized;
  143. background_task_runner_->PostTask(
  144. FROM_HERE,
  145. base::BindOnce(
  146. &CloseDatabaseSync, db_.get(), base::ThreadTaskRunnerHandle::Get(),
  147. base::BindOnce(&SqlStoreBase::CloseInternalDone,
  148. weak_ptr_factory_.GetWeakPtr(), std::move(db_))));
  149. }
  150. void SqlStoreBase::RescheduleClosingBefore() {
  151. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  152. FROM_HERE,
  153. base::BindOnce(&SqlStoreBase::CloseInternal,
  154. closing_weak_ptr_factory_.GetWeakPtr()),
  155. kClosingDelay);
  156. // Note: the time recorded for this trace step will include thread hop wait
  157. // times to the background thread and back.
  158. OnTaskRunComplete();
  159. }
  160. void SqlStoreBase::CloseInternalDone(DatabaseUniquePtr db) {
  161. db.reset();
  162. OnCloseComplete();
  163. }
  164. } // namespace offline_pages