| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef COBALT_STORAGE_STORAGE_MANAGER_H_ |
| #define COBALT_STORAGE_STORAGE_MANAGER_H_ |
| |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/timer.h" |
| #include "cobalt/storage/savegame_thread.h" |
| #include "cobalt/storage/sql_vfs.h" |
| #include "cobalt/storage/upgrade/upgrade_reader.h" |
| #include "cobalt/storage/virtual_file_system.h" |
| #include "sql/connection.h" |
| |
| namespace cobalt { |
| namespace storage { |
| class SqlContext; |
| |
| // StorageManager manages a SQLite database containing cookies and local |
| // storage data. On most platforms, this is written to disk as a savegame |
| // using platform APIs. On Linux/Windows, it's a regular file. |
| // Internally this runs two threads: one thread to perform blocking I/O, |
| // and one where SQL operations occur. Users are expected to access the |
| // database via an SqlContext, which can be obtained with GetSqlContext(). |
| // The callback to GetSqlCallback will run on the SQL thread. |
| // Operations on SqlContext will block the SQL thread until the savegame |
| // is loaded. |
| class StorageManager { |
| public: |
| // Support for "upgrade" of legacy save data that may have been generated by |
| // a platform other than Steel/Cobalt. If save data in the upgrade format is |
| // detected, the |OnUpgrade| method will be called on |upgrade_handler_|. |
| class UpgradeHandler { |
| public: |
| virtual ~UpgradeHandler() {} |
| virtual void OnUpgrade(StorageManager* storage, const char* data, |
| int size) = 0; |
| }; |
| |
| struct Options { |
| Savegame::Options savegame_options; |
| }; |
| |
| typedef base::Callback<void(SqlContext*)> SqlCallback; |
| |
| // Database version "2" indicates that this was created by Steel v1.x. |
| // Database version "0" indicates that this was created by v2.x beta, |
| // patches 0-3. Version "0" is a default from sqlite3 because these |
| // versions of the application did not set this value at all. |
| // Database version "3" indicates that the schema versions of individual |
| // tables should be tracked in SchemaTable. |
| static const int kDatabaseUserVersion = 3; |
| |
| // Schema-related error codes. See GetSchemaVersion(). |
| enum { |
| kSchemaTableIsNew = -1, |
| kSchemaVersionLost = -2, |
| }; |
| |
| StorageManager(scoped_ptr<UpgradeHandler> upgrade_handler, |
| const Options& options); |
| virtual ~StorageManager(); |
| |
| // Obtain the SqlContext for our database. |
| // |callback| will be called with an SqlContext that can be used to operate on |
| // the database. The callback will run on the storage manager's message loop. |
| void GetSqlContext(const SqlCallback& callback); |
| |
| // Schedule a write of our database to disk to happen at some point in the |
| // future after a change occurs. Multiple calls to Flush() do not necessarily |
| // result in multiple writes to disk. |
| // This call returns immediately. |
| void FlushOnChange(); |
| |
| // Triggers a write to disk to happen immediately. Each call to FlushNow() |
| // will result in a write to disk. |
| // |callback|, if provided, will be called when the I/O has completed, |
| // and will be run on the storage manager's IO thread. |
| // This call returns immediately. |
| void FlushNow(const base::Closure& callback); |
| |
| const Options& options() const { return options_; } |
| |
| UpgradeHandler* upgrade_handler() const { return upgrade_handler_.get(); } |
| |
| protected: |
| // Queues a flush to be executed as soon as possible. As soon as possible |
| // will be as soon as any existing flush completes, or right away if no |
| // existing flush is happening. Note that it is protected and virtual for |
| // white box testing purposes. |
| virtual void QueueFlush(const base::Closure& callback); |
| |
| private: |
| // SqlContext needs access to our internal APIs. |
| friend class SqlContext; |
| // Give StorageManagerTest access, so we can more easily test some internals. |
| friend class StorageManagerTest; |
| |
| // Flushes all queued flushes to the savegame thread. |
| virtual void FlushInternal(); |
| |
| // Initialize the SQLite database. This blocks until the savegame load is |
| // complete. |
| void FinishInit(); |
| |
| // Stops any timers that are currently running. |
| void StopFlushOnChangeTimers(); |
| |
| // Callback when flush timer has elapsed. |
| void OnFlushOnChangeTimerFired(); |
| |
| // Logic to be executed on the SQL thread when a flush completes. Will |
| // dispatch |flush_processing_callbacks_| callbacks and execute a new flush |
| // if |flush_requested_| is true. |
| void OnFlushIOCompletedSQLCallback(); |
| |
| // This function will not return until all queued I/O is completed. Since |
| // it will require the SQL message loop to process, it must be called from |
| // outside the SQL message loop (such as from StorageManager's destructor). |
| void FinishIO(); |
| |
| // Called by the destructor, to ensure we destroy certain objects on the |
| // sql thread. |
| void OnDestroy(); |
| |
| // Internal API for use by SqlContext. |
| sql::Connection* sql_connection(); |
| bool GetSchemaVersion(const char* table_name, int* schema_version); |
| void UpdateSchemaVersion(const char* table_name, int version); |
| |
| // Upgrade handler used if upgrade save data is detected. |
| scoped_ptr<UpgradeHandler> upgrade_handler_; |
| |
| // Configuration options for the Storage Manager. |
| Options options_; |
| |
| // Storage manager runs on its own thread. This is where SQL database |
| // operations are done. |
| scoped_ptr<base::Thread> sql_thread_; |
| scoped_refptr<base::MessageLoopProxy> sql_message_loop_; |
| |
| // An interface to the storage manager's SQL database that will run on |
| // the correct thread. |
| scoped_ptr<SqlContext> sql_context_; |
| |
| // The in-memory database connection. |
| scoped_ptr<sql::Connection> connection_; |
| |
| // Virtual file system that contains our in-memory SQLite database. |
| scoped_ptr<VirtualFileSystem> vfs_; |
| |
| // An interface between Sqlite and VirtualFileSystem. |
| scoped_ptr<SqlVfs> sql_vfs_; |
| |
| // When the savegame is loaded at startup, we keep the raw data around |
| // until we can initialize the database on the correct thread. |
| scoped_ptr<Savegame::ByteVector> loaded_raw_bytes_; |
| |
| // Timers that start running when FlushOnChange() is called. When the time |
| // elapses, we actually perform the write. This is a simple form of rate |
| // limiting I/O writes. |
| // |flush_on_last_change_timer_| is re-started on each change, enabling |
| // changes to collect if several happen within a short period of time. |
| // |flush_on_change_max_delay_timer_| starts on the first change and is never |
| // re-started, ensuring that the flush always occurs within its delay and |
| // cannot be pushed back indefinitely. |
| scoped_ptr<base::OneShotTimer<StorageManager> > flush_on_last_change_timer_; |
| scoped_ptr<base::OneShotTimer<StorageManager> > |
| flush_on_change_max_delay_timer_; |
| |
| // See comments for for kDatabaseUserVersion. |
| int loaded_database_version_; |
| // false until the SQL database is fully configured. |
| bool initialized_; |
| |
| // True if a flush is currently being processed on the storage message loop. |
| // In this case, we should not issue more flushes, but instead set |
| // |flush_requested_| to true to ensure that a new flush is submitted as |
| // soon as we are done processing the current one. |
| bool flush_processing_; |
| |
| // The queue of callbacks that are should be called when the current flush |
| // completes. If this is not empty, then |flush_processing_| must be true. |
| std::vector<base::Closure> flush_processing_callbacks_; |
| |
| // True if |flush_processing_| is true, but we would like to perform a new |
| // flush as soon as it completes. |
| bool flush_pending_; |
| |
| // The queue of callbacks that will be called when the flush that follows |
| // the current flush completes. If this is non-empty, then |flush_pending_| |
| // must be true. |
| std::vector<base::Closure> flush_pending_callbacks_; |
| |
| base::WaitableEvent no_flushes_pending_; |
| |
| // An object that wraps Savegame inside of an I/O thread so that we can |
| // flush data asynchronously. |
| scoped_ptr<SavegameThread> savegame_thread_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StorageManager); |
| }; |
| |
| // Proxy for accessing StorageManager's SQL database in a thread-safe way. |
| // All access to the StorageManager's database should be done via an SqlContext. |
| // Only the StorageManager can create this class. |
| class SqlContext { |
| public: |
| sql::Connection* sql_connection() const { |
| return storage_manager_->sql_connection(); |
| } |
| |
| // Get the schema version for the given table. |
| // Returns false if the table does not exist, otherwise returns true |
| // and writes the version number to the schema_version pointer. |
| // schema_version will be set to kSchemaTableIsNew if the table exists, |
| // but the schema table was newly created in this session. |
| // schema_version will be set to kSchemaVersionLost if the table exists, |
| // and the schema table existed once before, but has since been lost. |
| // In this case, the schema version cannot be known or directly inferred. |
| bool GetSchemaVersion(const char* table_name, int* schema_version) { |
| return storage_manager_->GetSchemaVersion(table_name, schema_version); |
| } |
| |
| // Updates the schema version for the given table. |
| // The version number must be greater than 0. |
| void UpdateSchemaVersion(const char* table_name, int version) { |
| return storage_manager_->UpdateSchemaVersion(table_name, version); |
| } |
| |
| void FlushOnChange() { storage_manager_->FlushOnChange(); } |
| |
| void FlushNow(const base::Closure& callback) { |
| storage_manager_->FlushNow(callback); |
| } |
| |
| private: |
| StorageManager* storage_manager_; |
| |
| explicit SqlContext(StorageManager* storage_manager) |
| : storage_manager_(storage_manager) {} |
| |
| friend StorageManager::StorageManager( |
| scoped_ptr<StorageManager::UpgradeHandler> upgrade_handler, |
| const Options& options); |
| DISALLOW_COPY_AND_ASSIGN(SqlContext); |
| }; |
| |
| } // namespace storage |
| } // namespace cobalt |
| |
| #endif // COBALT_STORAGE_STORAGE_MANAGER_H_ |