diff options
Diffstat (limited to 'toolkit/components/places/Shutdown.h')
-rw-r--r-- | toolkit/components/places/Shutdown.h | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/toolkit/components/places/Shutdown.h b/toolkit/components/places/Shutdown.h new file mode 100644 index 000000000..69023c608 --- /dev/null +++ b/toolkit/components/places/Shutdown.h @@ -0,0 +1,171 @@ +/* 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_Shutdown_h_ +#define mozilla_places_Shutdown_h_ + +#include "nsIAsyncShutdown.h" +#include "Database.h" +#include "nsProxyRelease.h" + +namespace mozilla { +namespace places { + +class Database; + +/** + * This is most of the code responsible for Places shutdown. + * + * PHASE 1 (Legacy clients shutdown) + * The shutdown procedure begins when the Database singleton receives + * profile-change-teardown (note that tests will instead notify nsNavHistory, + * that forwards the notification to the Database instance). + * Database::Observe first of all checks if initialization was completed + * properly, to avoid race conditions, then it notifies "places-shutdown" to + * legacy clients. Legacy clients are supposed to start and complete any + * shutdown critical work in the same tick, since we won't wait for them. + + * PHASE 2 (Modern clients shutdown) + * Modern clients should instead register as a blocker by passing a promise to + * nsPIPlacesDatabase::shutdownClient (for example see sanitize.js), so they + * block Places shutdown until the promise is resolved. + * When profile-change-teardown is observed by async shutdown, it calls + * ClientsShutdownBlocker::BlockShutdown. This class is registered as a teardown + * phase blocker in Database::Init (see Database::mClientsShutdown). + * ClientsShutdownBlocker::BlockShudown waits for all the clients registered + * through nsPIPlacesDatabase::shutdownClient. When all the clients are done, + * its `Done` method is invoked, and it stops blocking the shutdown phase, so + * that it can continue. + * + * PHASE 3 (Connection shutdown) + * ConnectionBlocker is registered as a profile-before-change blocker in + * Database::Init (see Database::mConnectionShutdown). + * When profile-before-change is observer by async shutdown, it calls + * ConnectionShutdownBlocker::BlockShutdown. + * This is the last chance for any Places internal work, like privacy cleanups, + * before the connection is closed. This a places-will-close-connection + * notification is sent to legacy clients that must complete any operation in + * the same tick, since we won't wait for them. + * Then the control is passed to Database::Shutdown, that executes some sanity + * checks, clears cached statements and proceeds with asyncClose. + * Once the connection is definitely closed, Database will call back + * ConnectionBlocker::Complete. At this point a final + * places-connection-closed notification is sent, for testing purposes. + */ + +/** + * A base AsyncShutdown blocker in charge of shutting down Places. + */ +class PlacesShutdownBlocker : public nsIAsyncShutdownBlocker +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIASYNCSHUTDOWNBLOCKER + + explicit PlacesShutdownBlocker(const nsString& aName); + + /** + * `true` if we have not started shutdown, i.e. if + * `BlockShutdown()` hasn't been called yet, false otherwise. + */ + static bool IsStarted() { + return sIsStarted; + } + + // The current state, used internally and for forensics/debugging purposes. + // Not all the states make sense for all the derived classes. + enum States { + NOT_STARTED, + // Execution of `BlockShutdown` in progress. + RECEIVED_BLOCK_SHUTDOWN, + + // Values specific to ClientsShutdownBlocker + // a. Set while we are waiting for clients to do their job and unblock us. + CALLED_WAIT_CLIENTS, + // b. Set when all the clients are done. + RECEIVED_DONE, + + // Values specific to ConnectionShutdownBlocker + // a. Set after we notified observers that Places is closing the connection. + NOTIFIED_OBSERVERS_PLACES_WILL_CLOSE_CONNECTION, + // b. Set after we pass control to Database::Shutdown, and wait for it to + // close the connection and call our `Complete` method when done. + CALLED_STORAGESHUTDOWN, + // c. Set when Database has closed the connection and passed control to + // us through `Complete`. + RECEIVED_STORAGESHUTDOWN_COMPLETE, + // d. We have notified observers that Places has closed the connection. + NOTIFIED_OBSERVERS_PLACES_CONNECTION_CLOSED, + }; + States State() { + return mState; + } + +protected: + // The blocker name, also used as barrier name. + nsString mName; + // The current state, see States. + States mState; + // The barrier optionally used to wait for clients. + nsMainThreadPtrHandle<nsIAsyncShutdownBarrier> mBarrier; + // The parent object who registered this as a blocker. + nsMainThreadPtrHandle<nsIAsyncShutdownClient> mParentClient; + + // As tests may resurrect a dead `Database`, we use a counter to + // give the instances of `PlacesShutdownBlocker` unique names. + uint16_t mCounter; + static uint16_t sCounter; + + static Atomic<bool> sIsStarted; + + virtual ~PlacesShutdownBlocker() {} +}; + +/** + * Blocker also used to wait for clients, through an owned barrier. + */ +class ClientsShutdownBlocker final : public PlacesShutdownBlocker + , public nsIAsyncShutdownCompletionCallback +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK + + explicit ClientsShutdownBlocker(); + + NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aParentClient) override; + + already_AddRefed<nsIAsyncShutdownClient> GetClient(); + +private: + ~ClientsShutdownBlocker() {} +}; + +/** + * Blocker used to wait when closing the database connection. + */ +class ConnectionShutdownBlocker final : public PlacesShutdownBlocker + , public mozIStorageCompletionCallback +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_MOZISTORAGECOMPLETIONCALLBACK + + NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aParentClient) override; + + explicit ConnectionShutdownBlocker(mozilla::places::Database* aDatabase); + +private: + ~ConnectionShutdownBlocker() {} + + // The owning database. + // The cycle is broken in method Complete(), once the connection + // has been closed by mozStorage. + RefPtr<mozilla::places::Database> mDatabase; +}; + +} // namespace places +} // namespace mozilla + +#endif // mozilla_places_Shutdown_h_ |