1 // Copyright 2018 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 
5 #include "components/leveldb_proto/internal/shared_proto_database.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/callback_helpers.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/task/post_task.h"
14 #include "base/task/thread_pool.h"
15 #include "components/leveldb_proto/internal/leveldb_database.h"
16 #include "components/leveldb_proto/internal/proto_database_selector.h"
17 #include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
18 #include "components/leveldb_proto/public/proto_database.h"
19 #include "components/leveldb_proto/public/proto_database_provider.h"
20 #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
21 
22 namespace leveldb_proto {
23 
24 namespace {
25 
26 const base::FilePath::CharType kMetadataDatabasePath[] =
27     FILE_PATH_LITERAL("metadata");
28 const int kMaxInitMetaDatabaseAttempts = 3;
29 
30 const char kGlobalMetadataKey[] = "__global";
31 
32 const char kSharedProtoDatabaseUmaName[] = "SharedDb";
33 
34 }  // namespace
35 
RunInitStatusCallbackOnCallingSequence(SharedProtoDatabase::SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner,Enums::InitStatus status,SharedDBMetadataProto::MigrationStatus migration_status,ProtoDatabaseSelector::ProtoDatabaseInitState metric)36 inline void RunInitStatusCallbackOnCallingSequence(
37     SharedProtoDatabase::SharedClientInitCallback callback,
38     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
39     Enums::InitStatus status,
40     SharedDBMetadataProto::MigrationStatus migration_status,
41     ProtoDatabaseSelector::ProtoDatabaseInitState metric) {
42   ProtoDatabaseSelector::RecordInitState(metric);
43   callback_task_runner->PostTask(
44       FROM_HERE, base::BindOnce(std::move(callback), status, migration_status));
45 }
46 
InitRequest(SharedClientInitCallback callback,const scoped_refptr<base::SequencedTaskRunner> & task_runner,const std::string & client_db_id)47 SharedProtoDatabase::InitRequest::InitRequest(
48     SharedClientInitCallback callback,
49     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
50     const std::string& client_db_id)
51     : callback(std::move(callback)),
52       task_runner(std::move(task_runner)),
53       client_db_id(client_db_id) {}
54 
55 SharedProtoDatabase::InitRequest::~InitRequest() = default;
56 
SharedProtoDatabase(const std::string & client_db_id,const base::FilePath & db_dir)57 SharedProtoDatabase::SharedProtoDatabase(const std::string& client_db_id,
58                                          const base::FilePath& db_dir)
59     : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
60           {base::MayBlock(),
61            // crbug/1006954 and crbug/976223 explain why one of the clients
62            // needs run in visible priority. Download DB is always loaded to
63            // check for in progress downloads at startup. So, always load shared
64            // db in USER_VISIBLE priority.
65            base::TaskPriority::USER_VISIBLE,
66            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
67       db_dir_(db_dir),
68       db_(std::make_unique<LevelDB>(client_db_id.c_str())),
69       db_wrapper_(std::make_unique<ProtoLevelDBWrapper>(task_runner_)),
70       metadata_db_wrapper_(
71           ProtoDatabaseProvider::GetUniqueDB<SharedDBMetadataProto>(
72               ProtoDbType::SHARED_DB_METADATA,
73               db_dir_.Append(base::FilePath(kMetadataDatabasePath)),
74               task_runner_)) {
75   DETACH_FROM_SEQUENCE(on_task_runner_);
76 }
77 
78 // All init functionality runs on the same SequencedTaskRunner, so any caller of
79 // this after a database Init will receive the correct status of the database.
80 // PostTaskAndReply is used to ensure that we call the Init callback on its
81 // original calling thread.
GetDatabaseInitStatusAsync(const std::string & client_db_id,Callbacks::InitStatusCallback callback)82 void SharedProtoDatabase::GetDatabaseInitStatusAsync(
83     const std::string& client_db_id,
84     Callbacks::InitStatusCallback callback) {
85   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
86   task_runner_->PostTask(
87       FROM_HERE, base::BindOnce(&SharedProtoDatabase::RunInitCallback, this,
88                                 std::move(callback),
89                                 base::SequencedTaskRunnerHandle::Get()));
90 }
91 
RunInitCallback(Callbacks::InitStatusCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)92 void SharedProtoDatabase::RunInitCallback(
93     Callbacks::InitStatusCallback callback,
94     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
95   callback_task_runner->PostTask(
96       FROM_HERE, base::BindOnce(std::move(callback), init_status_));
97 }
98 
UpdateClientMetadataAsync(const std::string & client_db_id,SharedDBMetadataProto::MigrationStatus migration_status,base::OnceCallback<void (bool)> callback)99 void SharedProtoDatabase::UpdateClientMetadataAsync(
100     const std::string& client_db_id,
101     SharedDBMetadataProto::MigrationStatus migration_status,
102     base::OnceCallback<void(bool)> callback) {
103   if (base::SequencedTaskRunnerHandle::Get() != task_runner_) {
104     task_runner_->PostTask(
105         FROM_HERE,
106         base::BindOnce(&SharedProtoDatabase::UpdateClientMetadataAsync, this,
107                        client_db_id, migration_status, std::move(callback)));
108     return;
109   }
110   auto update_entries = std::make_unique<
111       std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
112   SharedDBMetadataProto write_proto;
113   write_proto.set_corruptions(metadata_->corruptions());
114   write_proto.set_migration_status(migration_status);
115   update_entries->emplace_back(
116       std::make_pair(std::string(client_db_id), write_proto));
117 
118   metadata_db_wrapper_->UpdateEntries(
119       std::move(update_entries), std::make_unique<std::vector<std::string>>(),
120       std::move(callback));
121 }
122 
GetClientMetadataAsync(const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)123 void SharedProtoDatabase::GetClientMetadataAsync(
124     const std::string& client_db_id,
125     SharedClientInitCallback callback,
126     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
127   // |metadata_db_wrapper_| uses the same TaskRunner as Init and the main
128   // DB, so making this call directly here without PostTasking is safe. In
129   // addition, GetEntry uses PostTaskAndReply so the callback will be triggered
130   // on the calling sequence.
131   metadata_db_wrapper_->GetEntry(
132       std::string(client_db_id),
133       base::BindOnce(&SharedProtoDatabase::OnGetClientMetadata, this,
134                      client_db_id, std::move(callback),
135                      std::move(callback_task_runner)));
136 }
137 
138 // As mentioned above, |current_task_runner| is the appropriate calling sequence
139 // for the callback since the GetEntry call in GetClientMetadataAsync uses
140 // PostTaskAndReply.
OnGetClientMetadata(const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner,bool success,std::unique_ptr<SharedDBMetadataProto> proto)141 void SharedProtoDatabase::OnGetClientMetadata(
142     const std::string& client_db_id,
143     SharedClientInitCallback callback,
144     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
145     bool success,
146     std::unique_ptr<SharedDBMetadataProto> proto) {
147   // If fetching metadata failed, then ignore the error.
148   if (!success) {
149     RunInitStatusCallbackOnCallingSequence(
150         std::move(callback), std::move(callback_task_runner),
151         Enums::InitStatus::kOK, SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
152         ProtoDatabaseSelector::ProtoDatabaseInitState::
153             kSharedDbMetadataLoadFailed);
154     return;
155   }
156   if (!proto || !proto->has_migration_status()) {
157     UpdateClientMetadataAsync(
158         client_db_id, SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
159         base::BindOnce(
160             [](SharedClientInitCallback callback,
161                scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
162                bool update_success) {
163               // Do not care about update success since next time we will reset
164               // corruption and migration status to 0.
165               RunInitStatusCallbackOnCallingSequence(
166                   std::move(callback), std::move(callback_task_runner),
167                   Enums::InitStatus::kOK,
168                   SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
169                   ProtoDatabaseSelector::ProtoDatabaseInitState::
170                       kSharedDbMetadataWriteFailed);
171             },
172             std::move(callback), std::move(callback_task_runner)));
173     return;
174   }
175   // If we've made it here, we know that the current status of our database is
176   // OK. Make it return corrupt if the metadata disagrees.
177   bool is_corrupt = metadata_->corruptions() != proto->corruptions();
178   RunInitStatusCallbackOnCallingSequence(
179       std::move(callback), std::move(callback_task_runner),
180       is_corrupt ? Enums::InitStatus::kCorrupt : Enums::InitStatus::kOK,
181       proto->migration_status(),
182       is_corrupt ? ProtoDatabaseSelector::ProtoDatabaseInitState::
183                        kSharedDbClientCorrupt
184                  : ProtoDatabaseSelector::ProtoDatabaseInitState::
185                        kSharedDbClientSuccess);
186 }
187 
CheckCorruptionAndRunInitCallback(const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner,Enums::InitStatus status)188 void SharedProtoDatabase::CheckCorruptionAndRunInitCallback(
189     const std::string& client_db_id,
190     SharedClientInitCallback callback,
191     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
192     Enums::InitStatus status) {
193   if (init_status_ == Enums::InitStatus::kOK) {
194     GetClientMetadataAsync(client_db_id, std::move(callback),
195                            std::move(callback_task_runner));
196     return;
197   }
198   RunInitStatusCallbackOnCallingSequence(
199       std::move(callback), std::move(callback_task_runner), init_status_,
200       SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
201       ProtoDatabaseSelector::ProtoDatabaseInitState::kSharedLevelDbInitFailure);
202 }
203 
204 // Setting |create_if_missing| to false allows us to test whether or not the
205 // shared database already exists, useful for migrating data from the shared
206 // database to a unique database if it exists.
207 // All clients planning to use the shared database should be setting
208 // |create_if_missing| to true. Setting this to false can result in unexpected
209 // behaviour since the ordering of Init calls may matter if some calls are made
210 // with this set to true, and others false.
Init(bool create_if_missing,const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)211 void SharedProtoDatabase::Init(
212     bool create_if_missing,
213     const std::string& client_db_id,
214     SharedClientInitCallback callback,
215     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
216   // Try to create the db if any initialization request asked to do so.
217   create_if_missing_ = create_if_missing || create_if_missing_;
218 
219   switch (init_state_) {
220     case InitState::kNotAttempted:
221       outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
222           std::move(callback), std::move(callback_task_runner), client_db_id));
223 
224       init_state_ = InitState::kInProgress;
225       // First, try to initialize the metadata database.
226       InitMetadataDatabase(0 /* attempt */, false /* corruption */);
227       break;
228 
229     case InitState::kInProgress:
230       outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
231           std::move(callback), std::move(callback_task_runner), client_db_id));
232       break;
233 
234       // If we succeeded previously, just check for corruption status and run
235       // init callback.
236     case InitState::kSuccess:
237       CheckCorruptionAndRunInitCallback(client_db_id, std::move(callback),
238                                         std::move(callback_task_runner),
239                                         Enums::InitStatus::kOK);
240       break;
241 
242     // If we previously failed then we run the callback with kError.
243     case InitState::kFailure:
244       RunInitStatusCallbackOnCallingSequence(
245           std::move(callback), std::move(callback_task_runner),
246           Enums::InitStatus::kError,
247           SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
248           ProtoDatabaseSelector::ProtoDatabaseInitState::
249               kSharedLevelDbInitFailure);
250       break;
251 
252     case InitState::kNotFound:
253       if (create_if_missing_) {
254         // If the shared DB doesn't exist and we should create it if missing,
255         // then we skip initializing the metadata DB and initialize the shared
256         // DB directly.
257         DCHECK(metadata_);
258         init_state_ = InitState::kInProgress;
259         outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
260             std::move(callback), std::move(callback_task_runner),
261             client_db_id));
262         InitDatabase();
263       } else {
264         // If the shared DB doesn't exist and we shouldn't create it if missing,
265         // then we run the callback with kInvalidOperation (which is not found).
266         RunInitStatusCallbackOnCallingSequence(
267             std::move(callback), std::move(callback_task_runner),
268             Enums::InitStatus::kInvalidOperation,
269             SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
270             ProtoDatabaseSelector::ProtoDatabaseInitState::
271                 kSharedDbClientMissing);
272       }
273       break;
274   }
275 }
276 
ProcessInitRequests(Enums::InitStatus status)277 void SharedProtoDatabase::ProcessInitRequests(Enums::InitStatus status) {
278   DCHECK(!outstanding_init_requests_.empty());
279 
280   // The pairs are stored as (callback, callback_task_runner).
281   while (!outstanding_init_requests_.empty()) {
282     auto request = std::move(outstanding_init_requests_.front());
283     CheckCorruptionAndRunInitCallback(request->client_db_id,
284                                       std::move(request->callback),
285                                       std::move(request->task_runner), status);
286     outstanding_init_requests_.pop();
287   }
288 }
289 
290 // We allow some number of attempts to be made to initialize the metadata
291 // database because it's crucial for the operation of the shared database. In
292 // the event that the metadata DB is corrupt, at least one retry will be made
293 // so that we create the DB from scratch again.
294 // |corruption| lets us know whether the retries are because of corruption.
InitMetadataDatabase(int attempt,bool corruption)295 void SharedProtoDatabase::InitMetadataDatabase(int attempt, bool corruption) {
296   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
297 
298   if (attempt >= kMaxInitMetaDatabaseAttempts) {
299     // TODO(crbug/1003951): |attempt| is always 0, need to save it and do the
300     // retry, or delete it.
301     init_state_ = InitState::kFailure;
302     init_status_ = Enums::InitStatus::kError;
303     ProcessInitRequests(init_status_);
304     return;
305   }
306 
307   // TODO: figure out destroy on corruption param
308   metadata_db_wrapper_->Init(
309       base::BindOnce(&SharedProtoDatabase::OnMetadataInitComplete, this,
310                      attempt, corruption));
311 }
312 
OnMetadataInitComplete(int attempt,bool corruption,leveldb_proto::Enums::InitStatus status)313 void SharedProtoDatabase::OnMetadataInitComplete(
314     int attempt,
315     bool corruption,
316     leveldb_proto::Enums::InitStatus status) {
317   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
318 
319   bool success = status == Enums::kOK;
320 
321   if (!success) {
322     init_state_ = InitState::kFailure;
323     init_status_ = Enums::InitStatus::kError;
324     ProcessInitRequests(init_status_);
325     return;
326   }
327 
328   // Read or initialize the corruption count for this DB. If |corruption| is
329   // true, we initialize the counter to 1 right away so that all DBs are forced
330   // to treat the shared database as corrupt, we can't know for sure anymore.
331   metadata_db_wrapper_->GetEntry(
332       std::string(kGlobalMetadataKey),
333       base::BindOnce(&SharedProtoDatabase::OnGetGlobalMetadata, this,
334                      corruption));
335 }
336 
OnGetGlobalMetadata(bool corruption,bool success,std::unique_ptr<SharedDBMetadataProto> proto)337 void SharedProtoDatabase::OnGetGlobalMetadata(
338     bool corruption,
339     bool success,
340     std::unique_ptr<SharedDBMetadataProto> proto) {
341   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
342   if (success && proto) {
343     // It existed so let's update our internal |corruption_count_|
344     metadata_ = std::move(proto);
345     InitDatabase();
346     return;
347   }
348 
349   // We failed to get the global metadata, so we need to create it for the first
350   // time.
351   metadata_.reset(new SharedDBMetadataProto());
352   metadata_->set_corruptions(corruption ? 1U : 0U);
353   metadata_->clear_migration_status();
354   CommitUpdatedGlobalMetadata(
355       base::BindOnce(&SharedProtoDatabase::OnFinishCorruptionCountWrite, this));
356 }
357 
OnFinishCorruptionCountWrite(bool success)358 void SharedProtoDatabase::OnFinishCorruptionCountWrite(
359     bool success) {
360   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
361   // TODO(thildebr): Should we retry a few times if we fail this? It feels like
362   // if we fail to write this single value something serious happened with the
363   // metadata database.
364   if (!success) {
365     init_state_ = InitState::kFailure;
366     init_status_ = Enums::InitStatus::kError;
367     ProcessInitRequests(init_status_);
368     return;
369   }
370 
371   InitDatabase();
372 }
373 
InitDatabase()374 void SharedProtoDatabase::InitDatabase() {
375   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
376   auto options = CreateSimpleOptions();
377   options.create_if_missing = create_if_missing_;
378   db_wrapper_->SetMetricsId(kSharedProtoDatabaseUmaName);
379   // |db_wrapper_| uses the same SequencedTaskRunner that Init is called on,
380   // so OnDatabaseInit will be called on the same sequence after Init.
381   // This means any callers to Init using the same TaskRunner can guarantee that
382   // the InitState will be final after Init is called.
383   db_wrapper_->InitWithDatabase(
384       db_.get(), db_dir_, options, false /* destroy_on_corruption */,
385       base::BindOnce(&SharedProtoDatabase::OnDatabaseInit, this,
386                      create_if_missing_));
387 }
388 
OnDatabaseInit(bool create_if_missing,Enums::InitStatus status)389 void SharedProtoDatabase::OnDatabaseInit(bool create_if_missing,
390                                          Enums::InitStatus status) {
391   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
392 
393   // Update the corruption counter locally and in the database.
394   if (status == Enums::InitStatus::kCorrupt) {
395     // TODO(thildebr): Find a clean way to break this into a function to be used
396     // by OnGetCorruptionCountEntry.
397     // If this fails, we actually send back a failure to the init callback.
398     // This may be overkill, but what happens if we don't update this value?
399     // Again, it seems like a failure to update here will indicate something
400     // serious has gone wrong with the metadata database.
401     metadata_->set_corruptions(metadata_->corruptions() + 1);
402 
403     CommitUpdatedGlobalMetadata(base::BindOnce(
404         &SharedProtoDatabase::OnUpdateCorruptionCountAtInit, this));
405     return;
406   }
407 
408   // If the previous initialization didn't create the database but a following
409   // request tries to create the db. Redo the initialization and create db.
410   if (create_if_missing_ && !create_if_missing &&
411       status == Enums::InitStatus::kInvalidOperation) {
412     DCHECK(init_state_ == InitState::kInProgress ||
413            init_state_ == InitState::kNotFound);
414     InitDatabase();
415     return;
416   }
417 
418   init_status_ = status;
419 
420   switch (status) {
421     case Enums::InitStatus::kOK:
422       init_state_ = InitState::kSuccess;
423       break;
424     case Enums::InitStatus::kInvalidOperation:
425       DCHECK(!create_if_missing_);
426       init_state_ = InitState::kNotFound;
427       break;
428     case Enums::InitStatus::kError:
429     case Enums::InitStatus::kNotInitialized:
430     case Enums::InitStatus::kCorrupt:
431       init_state_ = InitState::kFailure;
432       break;
433   }
434 
435   ProcessInitRequests(status);
436 
437   if (init_state_ == InitState::kSuccess) {
438     // Hold on to shared db until the remove operation is done or Shutdown()
439     // clears the task.
440     Callbacks::UpdateCallback keep_shared_db_alive =
441         base::BindOnce([](scoped_refptr<SharedProtoDatabase>, bool) {},
442                        base::WrapRefCounted<>(this));
443     delete_obsolete_task_.Reset(base::BindOnce(
444         &SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients, this,
445         std::move(keep_shared_db_alive)));
446     task_runner_->PostDelayedTask(FROM_HERE, delete_obsolete_task_.callback(),
447                                   delete_obsolete_delay_);
448   }
449 }
450 
Shutdown()451 void SharedProtoDatabase::Shutdown() {
452   if (!task_runner_->RunsTasksInCurrentSequence()) {
453     task_runner_->PostTask(
454         FROM_HERE, base::BindOnce(&SharedProtoDatabase::Shutdown, this));
455     return;
456   }
457   delete_obsolete_task_.Cancel();
458 }
459 
OnUpdateCorruptionCountAtInit(bool success)460 void SharedProtoDatabase::OnUpdateCorruptionCountAtInit(bool success) {
461   // Return the success value of our write to update the corruption counter.
462   // This means that we return kError when the update fails, as a safeguard
463   // against clients trying to further persist data when something's gone
464   // wrong loading a single metadata proto.
465   init_state_ = success ? InitState::kSuccess : InitState::kFailure;
466   init_status_ =
467       success ? Enums::InitStatus::kCorrupt : Enums::InitStatus::kError;
468   ProcessInitRequests(init_status_);
469 }
470 
CommitUpdatedGlobalMetadata(Callbacks::UpdateCallback callback)471 void SharedProtoDatabase::CommitUpdatedGlobalMetadata(
472     Callbacks::UpdateCallback callback) {
473   auto update_entries = std::make_unique<
474       std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
475 
476   SharedDBMetadataProto write_proto;
477   write_proto.CheckTypeAndMergeFrom(*metadata_);
478   update_entries->emplace_back(
479       std::make_pair(std::string(kGlobalMetadataKey), write_proto));
480   metadata_db_wrapper_->UpdateEntries(
481       std::move(update_entries), std::make_unique<std::vector<std::string>>(),
482       std::move(callback));
483 }
484 
~SharedProtoDatabase()485 SharedProtoDatabase::~SharedProtoDatabase() {
486   task_runner_->DeleteSoon(FROM_HERE, std::move(db_));
487   task_runner_->DeleteSoon(FROM_HERE, std::move(metadata_db_wrapper_));
488 }
489 
GetClientInitCallback(base::OnceCallback<void (std::unique_ptr<SharedProtoDatabaseClient>,Enums::InitStatus)> callback,std::unique_ptr<SharedProtoDatabaseClient> client,Enums::InitStatus status,SharedDBMetadataProto::MigrationStatus migration_status)490 void GetClientInitCallback(
491     base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient>,
492                             Enums::InitStatus)> callback,
493     std::unique_ptr<SharedProtoDatabaseClient> client,
494     Enums::InitStatus status,
495     SharedDBMetadataProto::MigrationStatus migration_status) {
496   // |current_task_runner| is valid because Init already takes the current
497   // TaskRunner as a parameter and uses that to trigger this callback when it's
498   // finished.
499   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
500   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
501   if (status != Enums::InitStatus::kOK && status != Enums::InitStatus::kCorrupt)
502     client.reset();
503   // Set migration status of client. The metadata database was already updated.
504   if (client)
505     client->set_migration_status(migration_status);
506   current_task_runner->PostTask(
507       FROM_HERE,
508       base::BindOnce(std::move(callback), std::move(client), status));
509 }
510 
GetClientAsync(ProtoDbType db_type,bool create_if_missing,base::OnceCallback<void (std::unique_ptr<SharedProtoDatabaseClient>,Enums::InitStatus)> callback)511 void SharedProtoDatabase::GetClientAsync(
512     ProtoDbType db_type,
513     bool create_if_missing,
514     base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient>,
515                             Enums::InitStatus)> callback) {
516   auto client = GetClientInternal(db_type);
517   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
518   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
519   SharedProtoDatabaseClient* client_ptr = client.get();
520   task_runner_->PostTask(
521       FROM_HERE,
522       base::BindOnce(&SharedProtoDatabase::Init, this, create_if_missing,
523                      client_ptr->client_db_id(),
524                      base::BindOnce(&GetClientInitCallback, std::move(callback),
525                                     std::move(client)),
526                      std::move(current_task_runner)));
527 }
528 
529 // TODO(thildebr): Need to pass the client name into this call as well, and use
530 // it with the pending requests too so we can clean up the database.
531 std::unique_ptr<SharedProtoDatabaseClient>
GetClientForTesting(ProtoDbType db_type,bool create_if_missing,SharedClientInitCallback callback)532 SharedProtoDatabase::GetClientForTesting(ProtoDbType db_type,
533                                          bool create_if_missing,
534                                          SharedClientInitCallback callback) {
535   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
536   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
537   auto client = GetClientInternal(db_type);
538   task_runner_->PostTask(
539       FROM_HERE,
540       base::BindOnce(&SharedProtoDatabase::Init, this, create_if_missing,
541                      client->client_db_id(), std::move(callback),
542                      std::move(current_task_runner)));
543   return client;
544 }
545 
546 std::unique_ptr<SharedProtoDatabaseClient>
GetClientInternal(ProtoDbType db_type)547 SharedProtoDatabase::GetClientInternal(ProtoDbType db_type) {
548   return base::WrapUnique(new SharedProtoDatabaseClient(
549       std::make_unique<ProtoLevelDBWrapper>(task_runner_, db_.get()), db_type,
550       this));
551 }
552 
DestroyObsoleteSharedProtoDatabaseClients(Callbacks::UpdateCallback done)553 void SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients(
554     Callbacks::UpdateCallback done) {
555   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
556   // Create a ProtoLevelDBWrapper just like we create for each client, for
557   // deleting data from obsolete clients. It is fine to use the same wrapper to
558   // clear data from all clients. This object will be destroyed after clearing
559   // data for all these clients.
560   auto db_wrapper =
561       std::make_unique<ProtoLevelDBWrapper>(task_runner_, db_.get());
562   SharedProtoDatabaseClient::DestroyObsoleteSharedProtoDatabaseClients(
563       std::move(db_wrapper), std::move(done));
564 }
565 
GetLevelDBForTesting() const566 LevelDB* SharedProtoDatabase::GetLevelDBForTesting() const {
567   return db_.get();
568 }
569 
570 }  // namespace leveldb_proto
571