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