Source code

Revision control

Other Tools

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