[go: nahoru, domu]

blob: e461aff9d6b4c633d3444a4138382abc1d48087e [file] [log] [blame]
// Copyright 2018 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 COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_H_
#define COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_H_
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/cancelable_callback.h"
#include "base/component_export.h"
#include "base/containers/queue.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "components/leveldb_proto/internal/proto/shared_db_metadata.pb.h"
#include "components/leveldb_proto/internal/shared_proto_database_client.h"
#include "components/leveldb_proto/public/proto_database.h"
namespace leveldb_proto {
// Controls a single LevelDB database to be used by many clients, and provides
// a way to get SharedProtoDatabaseClients that allow shared access to the
// underlying single database.
class COMPONENT_EXPORT(LEVELDB_PROTO) SharedProtoDatabase
: public base::RefCountedThreadSafe<SharedProtoDatabase> {
public:
using SharedClientInitCallback =
base::OnceCallback<void(Enums::InitStatus,
SharedDBMetadataProto::MigrationStatus)>;
SharedProtoDatabase(const SharedProtoDatabase&) = delete;
SharedProtoDatabase& operator=(const SharedProtoDatabase&) = delete;
// Always returns a SharedProtoDatabaseClient pointer, but that should ONLY
// be used if the callback returns success.
std::unique_ptr<SharedProtoDatabaseClient> GetClientForTesting(
ProtoDbType db_type,
bool create_if_missing,
SharedClientInitCallback callback);
// A version of GetClient that returns the client in a callback instead of
// giving back a client instance immediately.
void GetClientAsync(
ProtoDbType db_type,
bool create_if_missing,
base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient>,
Enums::InitStatus)> callback);
void GetDatabaseInitStatusAsync(const std::string& client_db_id,
Callbacks::InitStatusCallback callback);
void UpdateClientMetadataAsync(
const std::string& client_db_id,
SharedDBMetadataProto::MigrationStatus migration_status,
Callbacks::UpdateCallback callback);
protected:
SharedProtoDatabase(const std::string& client_db_id,
const base::FilePath& db_dir);
virtual ~SharedProtoDatabase();
private:
friend class base::RefCountedThreadSafe<SharedProtoDatabase>;
friend class ProtoDatabaseProvider;
template <typename T>
friend class ProtoDatabaseImplTest;
friend class SharedProtoDatabaseTest;
friend class SharedProtoDatabaseClientTest;
friend class TestSharedProtoDatabase;
friend class TestSharedProtoDatabaseClient;
FRIEND_TEST_ALL_PREFIXES(SharedProtoDatabaseTest,
CancelDeleteObsoleteClients);
FRIEND_TEST_ALL_PREFIXES(SharedProtoDatabaseTest, DeleteObsoleteClients);
enum InitState {
// Initialization hasn't been attempted.
kNotAttempted,
// Initialization is in progress, new requests will be enqueued.
kInProgress,
// Initialization successful, new requests will return existing DB.
kSuccess,
// Initialization failed, new requests will return InitStatus::kError.
kFailure,
// Shared database doesn't exist, new requests with create_if_missing ==
// true will attempt to create it, if create_if_missing == false then will
// return InitStatus::kInvalidOperation.
kNotFound,
};
struct InitRequest {
InitRequest(SharedClientInitCallback callback,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
const std::string& client_db_id);
~InitRequest();
SharedClientInitCallback callback;
scoped_refptr<base::SequencedTaskRunner> task_runner;
std::string client_db_id;
};
// Make sure to give enough time after startup so that we have less chance of
// affecting startup or navigations.
static const base::TimeDelta kDelayToClearObsoleteDatabase;
void Shutdown();
void ProcessInitRequests(Enums::InitStatus status);
std::unique_ptr<SharedProtoDatabaseClient> GetClientInternal(
ProtoDbType db_type);
void OnGetClientMetadata(
const std::string& client_db_id,
SharedClientInitCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
bool success,
std::unique_ptr<SharedDBMetadataProto> proto);
// |callback_task_runner| should be the same sequence that Init was called
// from.
virtual void Init(
bool create_if_missing,
const std::string& client_db_id,
SharedClientInitCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
void InitMetadataDatabase(int attempt, bool corruption);
void OnMetadataInitComplete(int attempt,
bool corruption,
leveldb_proto::Enums::InitStatus status);
void OnGetGlobalMetadata(bool corruption,
bool success,
std::unique_ptr<SharedDBMetadataProto> proto);
void OnWriteMetadataAtInit(bool success);
void OnDestroySharedDatabase(bool success);
void InitDatabase();
void OnDatabaseInit(bool create_if_missing, Enums::InitStatus status);
void CheckCorruptionAndRunInitCallback(
const std::string& client_db_id,
SharedClientInitCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
Enums::InitStatus status);
void GetClientMetadataAsync(
const std::string& client_db_id,
SharedClientInitCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
void OnUpdateCorruptionCountAtInit(bool success);
void CommitUpdatedGlobalMetadata(Callbacks::UpdateCallback callback);
void RunInitCallback(
Callbacks::InitStatusCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
// |done| will be called on |task_runner|.
virtual void DestroyObsoleteSharedProtoDatabaseClients(
Callbacks::UpdateCallback done);
LevelDB* GetLevelDBForTesting() const;
void SetDeleteObsoleteDelayForTesting(base::TimeDelta delay);
scoped_refptr<base::SequencedTaskRunner> database_task_runner_for_testing()
const {
return task_runner_;
}
SEQUENCE_CHECKER(on_task_runner_);
InitState init_state_ = InitState::kNotAttempted;
// This TaskRunner is used to properly sequence Init calls and checks for the
// current init state. When clients request the current InitState as part of
// their call to their Init function, the request is put into this TaskRunner.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::FilePath db_dir_;
std::unique_ptr<LevelDB> db_;
std::unique_ptr<ProtoLevelDBWrapper> db_wrapper_;
std::unique_ptr<ProtoDatabase<SharedDBMetadataProto>> metadata_db_wrapper_;
std::unique_ptr<SharedDBMetadataProto> metadata_;
// Used to return to the Init callback in the case of an error, so we can
// report corruptions.
Enums::InitStatus init_status_ = Enums::InitStatus::kNotInitialized;
base::queue<std::unique_ptr<InitRequest>> outstanding_init_requests_;
bool create_if_missing_ = false;
base::TimeDelta delete_obsolete_delay_ = base::Seconds(120);
base::Lock delete_obsolete_delay_lock_;
base::CancelableOnceClosure delete_obsolete_task_;
};
} // namespace leveldb_proto
#endif // COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_H_