summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/Shutdown.h
blob: 69023c60896258ec3e2ce272fee3154f234f6fba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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_