| // 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. | 
 |   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(); | 
 |  | 
 |   // This function will immediately the on change timers if they are running. | 
 |   void FireRunningOnChangeTimers(); | 
 |  | 
 |   // 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_ |