Coverage Report

Created: 2018-09-25 14:53

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