Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "Shutdown.h"
6 : #include "mozilla/Unused.h"
7 :
8 : namespace mozilla {
9 : namespace places {
10 :
11 : uint16_t PlacesShutdownBlocker::sCounter = 0;
12 : Atomic<bool> PlacesShutdownBlocker::sIsStarted(false);
13 :
14 2 : PlacesShutdownBlocker::PlacesShutdownBlocker(const nsString& aName)
15 : : mName(aName)
16 : , mState(NOT_STARTED)
17 2 : , mCounter(sCounter++)
18 : {
19 2 : MOZ_ASSERT(NS_IsMainThread());
20 : // During tests, we can end up with the Database singleton being resurrected.
21 : // Make sure that each instance of DatabaseShutdown has a unique name.
22 2 : if (mCounter > 1) {
23 0 : mName.AppendInt(mCounter);
24 : }
25 2 : }
26 :
27 : // nsIAsyncShutdownBlocker
28 : NS_IMETHODIMP
29 8 : PlacesShutdownBlocker::GetName(nsAString& aName)
30 : {
31 8 : aName = mName;
32 8 : return NS_OK;
33 : }
34 :
35 : // nsIAsyncShutdownBlocker
36 : NS_IMETHODIMP
37 0 : PlacesShutdownBlocker::GetState(nsIPropertyBag** _state)
38 : {
39 0 : NS_ENSURE_ARG_POINTER(_state);
40 :
41 : nsCOMPtr<nsIWritablePropertyBag2> bag =
42 0 : do_CreateInstance("@mozilla.org/hash-property-bag;1");
43 0 : NS_ENSURE_TRUE(bag, NS_ERROR_OUT_OF_MEMORY);
44 0 : bag.forget(_state);
45 :
46 : // Put `mState` in field `progress`
47 0 : RefPtr<nsVariant> progress = new nsVariant();
48 0 : nsresult rv = progress->SetAsUint8(mState);
49 0 : if (NS_WARN_IF(NS_FAILED(rv))) return rv;
50 0 : rv = static_cast<nsIWritablePropertyBag2*>(*_state)->SetPropertyAsInterface(
51 0 : NS_LITERAL_STRING("progress"), progress);
52 0 : if (NS_WARN_IF(NS_FAILED(rv))) return rv;
53 :
54 : // Put `mBarrier`'s state in field `barrier`, if possible
55 0 : if (!mBarrier) {
56 0 : return NS_OK;
57 : }
58 0 : nsCOMPtr<nsIPropertyBag> barrierState;
59 0 : rv = mBarrier->GetState(getter_AddRefs(barrierState));
60 0 : if (NS_FAILED(rv)) {
61 0 : return NS_OK;
62 : }
63 :
64 0 : RefPtr<nsVariant> barrier = new nsVariant();
65 0 : rv = barrier->SetAsInterface(NS_GET_IID(nsIPropertyBag), barrierState);
66 0 : if (NS_WARN_IF(NS_FAILED(rv))) return rv;
67 0 : rv = static_cast<nsIWritablePropertyBag2*>(*_state)->SetPropertyAsInterface(
68 0 : NS_LITERAL_STRING("Barrier"), barrier);
69 0 : if (NS_WARN_IF(NS_FAILED(rv))) return rv;
70 :
71 0 : return NS_OK;
72 : }
73 :
74 : // nsIAsyncShutdownBlocker
75 : NS_IMETHODIMP
76 0 : PlacesShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
77 : {
78 0 : MOZ_ASSERT(false, "should always be overridden");
79 : return NS_ERROR_NOT_IMPLEMENTED;
80 : }
81 :
82 26 : NS_IMPL_ISUPPORTS(
83 : PlacesShutdownBlocker,
84 : nsIAsyncShutdownBlocker
85 : )
86 :
87 : ////////////////////////////////////////////////////////////////////////////////
88 :
89 1 : ClientsShutdownBlocker::ClientsShutdownBlocker()
90 1 : : PlacesShutdownBlocker(NS_LITERAL_STRING("Places Clients shutdown"))
91 : {
92 1 : MOZ_ASSERT(NS_IsMainThread());
93 : // Create a barrier that will be exposed to clients through GetClient(), so
94 : // they can block Places shutdown.
95 2 : nsCOMPtr<nsIAsyncShutdownService> asyncShutdown = services::GetAsyncShutdown();
96 1 : MOZ_ASSERT(asyncShutdown);
97 1 : if (asyncShutdown) {
98 2 : nsCOMPtr<nsIAsyncShutdownBarrier> barrier;
99 1 : MOZ_ALWAYS_SUCCEEDS(asyncShutdown->MakeBarrier(mName, getter_AddRefs(barrier)));
100 : mBarrier = new nsMainThreadPtrHolder<nsIAsyncShutdownBarrier>(
101 2 : "ClientsShutdownBlocker::mBarrier", barrier);
102 : }
103 1 : }
104 :
105 : already_AddRefed<nsIAsyncShutdownClient>
106 0 : ClientsShutdownBlocker::GetClient()
107 : {
108 0 : nsCOMPtr<nsIAsyncShutdownClient> client;
109 0 : if (mBarrier) {
110 0 : MOZ_ALWAYS_SUCCEEDS(mBarrier->GetClient(getter_AddRefs(client)));
111 : }
112 0 : return client.forget();
113 : }
114 :
115 : // nsIAsyncShutdownBlocker
116 : NS_IMETHODIMP
117 0 : ClientsShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
118 : {
119 0 : MOZ_ASSERT(NS_IsMainThread());
120 : mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
121 0 : "ClientsShutdownBlocker::mParentClient", aParentClient);
122 0 : mState = RECEIVED_BLOCK_SHUTDOWN;
123 :
124 0 : if (NS_WARN_IF(!mBarrier)) {
125 0 : return NS_ERROR_NOT_AVAILABLE;
126 : }
127 :
128 : // Wait until all the clients have removed their blockers.
129 0 : MOZ_ALWAYS_SUCCEEDS(mBarrier->Wait(this));
130 :
131 0 : mState = CALLED_WAIT_CLIENTS;
132 0 : return NS_OK;
133 : }
134 :
135 : // nsIAsyncShutdownCompletionCallback
136 : NS_IMETHODIMP
137 0 : ClientsShutdownBlocker::Done()
138 : {
139 : // At this point all the clients are done, we can stop blocking the shutdown
140 : // phase.
141 0 : mState = RECEIVED_DONE;
142 :
143 : // mParentClient is nullptr in tests.
144 0 : if (mParentClient) {
145 0 : nsresult rv = mParentClient->RemoveBlocker(this);
146 0 : if (NS_WARN_IF(NS_FAILED(rv))) return rv;
147 0 : mParentClient = nullptr;
148 : }
149 0 : mBarrier = nullptr;
150 0 : return NS_OK;
151 : }
152 :
153 13 : NS_IMPL_ISUPPORTS_INHERITED(
154 : ClientsShutdownBlocker,
155 : PlacesShutdownBlocker,
156 : nsIAsyncShutdownCompletionCallback
157 : )
158 :
159 : ////////////////////////////////////////////////////////////////////////////////
160 :
161 1 : ConnectionShutdownBlocker::ConnectionShutdownBlocker(Database* aDatabase)
162 2 : : PlacesShutdownBlocker(NS_LITERAL_STRING("Places Connection shutdown"))
163 2 : , mDatabase(aDatabase)
164 : {
165 : // Do nothing.
166 1 : }
167 :
168 : // nsIAsyncShutdownBlocker
169 : NS_IMETHODIMP
170 0 : ConnectionShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
171 : {
172 0 : MOZ_ASSERT(NS_IsMainThread());
173 : mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
174 0 : "ConnectionShutdownBlocker::mParentClient", aParentClient);
175 0 : mState = RECEIVED_BLOCK_SHUTDOWN;
176 : // Annotate that Database shutdown started.
177 0 : sIsStarted = true;
178 :
179 : // Fire internal database closing notification.
180 0 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
181 0 : MOZ_ASSERT(os);
182 0 : if (os) {
183 0 : Unused << os->NotifyObservers(nullptr, TOPIC_PLACES_WILL_CLOSE_CONNECTION, nullptr);
184 : }
185 0 : mState = NOTIFIED_OBSERVERS_PLACES_WILL_CLOSE_CONNECTION;
186 :
187 : // At this stage, any use of this database is forbidden. Get rid of
188 : // `gDatabase`. Note, however, that the database could be
189 : // resurrected. This can happen in particular during tests.
190 0 : MOZ_ASSERT(Database::gDatabase == nullptr || Database::gDatabase == mDatabase);
191 0 : Database::gDatabase = nullptr;
192 :
193 : // Database::Shutdown will invoke Complete once the connection is closed.
194 0 : mDatabase->Shutdown();
195 0 : mState = CALLED_STORAGESHUTDOWN;
196 0 : return NS_OK;
197 : }
198 :
199 : // mozIStorageCompletionCallback
200 : NS_IMETHODIMP
201 0 : ConnectionShutdownBlocker::Complete(nsresult, nsISupports*)
202 : {
203 0 : MOZ_ASSERT(NS_IsMainThread());
204 0 : mState = RECEIVED_STORAGESHUTDOWN_COMPLETE;
205 :
206 : // The connection is closed, the Database has no more use, so we can break
207 : // possible cycles.
208 0 : mDatabase = nullptr;
209 :
210 : // Notify the connection has gone.
211 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
212 0 : MOZ_ASSERT(os);
213 0 : if (os) {
214 0 : MOZ_ALWAYS_SUCCEEDS(os->NotifyObservers(nullptr,
215 : TOPIC_PLACES_CONNECTION_CLOSED,
216 : nullptr));
217 : }
218 0 : mState = NOTIFIED_OBSERVERS_PLACES_CONNECTION_CLOSED;
219 :
220 : // mParentClient is nullptr in tests
221 0 : if (mParentClient) {
222 0 : nsresult rv = mParentClient->RemoveBlocker(this);
223 0 : if (NS_WARN_IF(NS_FAILED(rv))) return rv;
224 0 : mParentClient = nullptr;
225 : }
226 0 : return NS_OK;
227 : }
228 :
229 13 : NS_IMPL_ISUPPORTS_INHERITED(
230 : ConnectionShutdownBlocker,
231 : PlacesShutdownBlocker,
232 : mozIStorageCompletionCallback
233 : )
234 :
235 : } // namespace places
236 : } // namespace mozilla
|