/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_places_ConcurrentConnection_h_ #define mozilla_places_ConcurrentConnection_h_ #include "mozilla/storage/StatementCache.h" #include "mozIStorageCompletionCallback.h" #include "mozIStorageStatementCallback.h" #include "Helpers.h" #include "nsCOMPtr.h" #include "nsDeque.h" #include "nsIAsyncShutdown.h" #include "nsIObserver.h" #include "nsISupportsImpl.h" #include "nsWeakReference.h" namespace mozilla::places { /** * Tracks all the necessary information to asynchronously run a query, and * call back once done. */ struct PendingQuery final { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PendingQuery); PendingQuery(const nsCString& aSQL, PendingStatementCallback* aCallback) : mSQL(aSQL), mCallback(aCallback) {} nsCString mSQL; RefPtr mCallback; private: ~PendingQuery() = default; }; /** * Wraps a concurrent SQLite connection, that has zero dependencies on Places. * This is useful to read from the database without fully initializing the * whole Places subsystem, e.g. link coloring, favicons... * * Since this is lacking any capability of setting up the database file, if it * doesn't exist, or has an outdated schema version, it will queue up requests * and await for Places to start up fully. */ class ConcurrentConnection final : public nsIObserver, public nsSupportsWeakReference, public nsIAsyncShutdownBlocker, public mozIStorageCompletionCallback, public mozIStorageStatementCallback { using StatementCache = mozilla::storage::StatementCache; using AsyncStatementCache = mozilla::storage::StatementCache; public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSIASYNCSHUTDOWNBLOCKER NS_DECL_MOZISTORAGECOMPLETIONCALLBACK NS_DECL_MOZISTORAGESTATEMENTCALLBACK /** * It is normally not necessary to have more than one instance of this, so * it's suggested to use the GetInstance static helper below to instance * this class. */ ConcurrentConnection(); /** * Get a pointer to the singleton instance. */ static Maybe GetInstance(); /** * Enqueue a query or a Runnable. * Each consumers should only use one of these for proper serialization. * TODO: Unify the queues, maybe using a Union. */ void Queue(const nsCString& aSQL, PendingStatementCallback* aCallback); void Queue(Runnable* aRunnable); /** * Gets a cached synchronous statement on the helper thread. * * @param aQuery * nsCString of SQL query. * @returns The cached statement. * @note Always null check the result. * @note Always use a scoper to reset the statement. */ already_AddRefed GetStatementOnHelperThread( const nsCString& aQuery); private: /** * Gets a cached asynchronous statement on the main thread. * This is private, as you normally should use Queue. * * @param aQuery * nsCString of SQL query. * @returns The cached statement. * @note Always null check the result. * @note As this returns an async statement, it's not necessary to use a * scoper, as it will be reset automatically after execution. */ already_AddRefed GetStatement( const nsCString& aQuery); /** * Try to consume the queue. */ void TryToConsumeQueues(); /** * Try to open a database connection. * This may arguably fail, for example if the database was not created yet, * or has an outdated schema version. In that case this component will try * again later, once it is notified the Places subsystem is up and running. */ void TryToOpenConnection(); /** * Setups the connection, initializing functions and attaching other * databases. */ void SetupConnection(); /** * Close the currently tracked connection. */ void CloseConnection(); void CloseConnectionComplete(nsresult rv); /** * Shutdown and cleanup. * @note After invoking this the component cannot be resurrected. */ void Shutdown(); /** * Helper to attach a database file. */ nsresult AttachDatabase(const nsString& aFileName, const nsCString& aSchemaName); static ConcurrentConnection* gConcurrentConnection; ~ConcurrentConnection() = default; // The current state, used to track progress in AsyncShutdown. enum States { NOT_STARTED = 0, AWAITING_DATABASE_READY = 1, READY = 2, SHUTTING_DOWN = 3, AWAITING_DATABASE_CLOSED = 4, CLOSED = 5, }; States mState = NOT_STARTED; bool mIsOpening = false; bool mPlacesIsInitialized = false; bool mRetryOpening = true; bool mIsShuttingDown = false; bool mIsConnectionReady = false; int32_t mSchemaVersion = -1; // Ideally this should be a mozIStorageAsyncConnection, as that would give us // additional checks we're not abusing the main-thread, though that would // limit us excessively, since `StatementCache` and `CreateStatement` only // work on a full-fledged Connection object. We'll have to take particular // care of not touching the main-thread. nsCOMPtr mConn; /** * The parent object who registered this as a blocker. */ nsCOMPtr mShutdownBarrierClient; /** * Collections of queries and runnables to be executed. */ nsRefPtrDeque mPendingQueries; nsRefPtrDeque mPendingRunnables; /** * Statements caches. */ UniquePtr mAsyncStatements; UniquePtr mHelperThreadStatements; }; } // namespace mozilla::places #endif // mozilla_places_ConcurrentConnection_h_