|
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 "nsNSSShutDown.h" |
|
6 #include "nsCOMPtr.h" |
|
7 |
|
8 using namespace mozilla; |
|
9 |
|
10 #ifdef PR_LOGGING |
|
11 extern PRLogModuleInfo* gPIPNSSLog; |
|
12 #endif |
|
13 |
|
14 struct ObjectHashEntry : PLDHashEntryHdr { |
|
15 nsNSSShutDownObject *obj; |
|
16 }; |
|
17 |
|
18 static bool |
|
19 ObjectSetMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, |
|
20 const void *key) |
|
21 { |
|
22 const ObjectHashEntry *entry = static_cast<const ObjectHashEntry*>(hdr); |
|
23 return entry->obj == static_cast<const nsNSSShutDownObject*>(key); |
|
24 } |
|
25 |
|
26 static bool |
|
27 ObjectSetInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr, |
|
28 const void *key) |
|
29 { |
|
30 ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr); |
|
31 entry->obj = const_cast<nsNSSShutDownObject*>(static_cast<const nsNSSShutDownObject*>(key)); |
|
32 return true; |
|
33 } |
|
34 |
|
35 static const PLDHashTableOps gSetOps = { |
|
36 PL_DHashAllocTable, |
|
37 PL_DHashFreeTable, |
|
38 PL_DHashVoidPtrKeyStub, |
|
39 ObjectSetMatchEntry, |
|
40 PL_DHashMoveEntryStub, |
|
41 PL_DHashClearEntryStub, |
|
42 PL_DHashFinalizeStub, |
|
43 ObjectSetInitEntry |
|
44 }; |
|
45 |
|
46 nsNSSShutDownList *nsNSSShutDownList::singleton = nullptr; |
|
47 |
|
48 nsNSSShutDownList::nsNSSShutDownList() |
|
49 :mListLock("nsNSSShutDownList.mListLock") |
|
50 { |
|
51 mActiveSSLSockets = 0; |
|
52 mPK11LogoutCancelObjects.ops = nullptr; |
|
53 mObjects.ops = nullptr; |
|
54 PL_DHashTableInit(&mObjects, &gSetOps, nullptr, |
|
55 sizeof(ObjectHashEntry), 16); |
|
56 PL_DHashTableInit(&mPK11LogoutCancelObjects, &gSetOps, nullptr, |
|
57 sizeof(ObjectHashEntry), 16); |
|
58 } |
|
59 |
|
60 nsNSSShutDownList::~nsNSSShutDownList() |
|
61 { |
|
62 if (mObjects.ops) { |
|
63 PL_DHashTableFinish(&mObjects); |
|
64 mObjects.ops = nullptr; |
|
65 } |
|
66 if (mPK11LogoutCancelObjects.ops) { |
|
67 PL_DHashTableFinish(&mPK11LogoutCancelObjects); |
|
68 mPK11LogoutCancelObjects.ops = nullptr; |
|
69 } |
|
70 PR_ASSERT(this == singleton); |
|
71 singleton = nullptr; |
|
72 } |
|
73 |
|
74 void nsNSSShutDownList::remember(nsNSSShutDownObject *o) |
|
75 { |
|
76 if (!singleton) |
|
77 return; |
|
78 |
|
79 PR_ASSERT(o); |
|
80 MutexAutoLock lock(singleton->mListLock); |
|
81 PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_ADD); |
|
82 } |
|
83 |
|
84 void nsNSSShutDownList::forget(nsNSSShutDownObject *o) |
|
85 { |
|
86 if (!singleton) |
|
87 return; |
|
88 |
|
89 PR_ASSERT(o); |
|
90 MutexAutoLock lock(singleton->mListLock); |
|
91 PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_REMOVE); |
|
92 } |
|
93 |
|
94 void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o) |
|
95 { |
|
96 if (!singleton) |
|
97 return; |
|
98 |
|
99 PR_ASSERT(o); |
|
100 MutexAutoLock lock(singleton->mListLock); |
|
101 PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_ADD); |
|
102 } |
|
103 |
|
104 void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o) |
|
105 { |
|
106 if (!singleton) |
|
107 return; |
|
108 |
|
109 PR_ASSERT(o); |
|
110 MutexAutoLock lock(singleton->mListLock); |
|
111 PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_REMOVE); |
|
112 } |
|
113 |
|
114 void nsNSSShutDownList::trackSSLSocketCreate() |
|
115 { |
|
116 if (!singleton) |
|
117 return; |
|
118 |
|
119 MutexAutoLock lock(singleton->mListLock); |
|
120 ++singleton->mActiveSSLSockets; |
|
121 } |
|
122 |
|
123 void nsNSSShutDownList::trackSSLSocketClose() |
|
124 { |
|
125 if (!singleton) |
|
126 return; |
|
127 |
|
128 MutexAutoLock lock(singleton->mListLock); |
|
129 --singleton->mActiveSSLSockets; |
|
130 } |
|
131 |
|
132 bool nsNSSShutDownList::areSSLSocketsActive() |
|
133 { |
|
134 if (!singleton) { |
|
135 // I'd rather prefer to be pessimistic and return true. |
|
136 // However, maybe we will get called at a time when the singleton |
|
137 // has already been freed, and returning true would bring up an |
|
138 // unnecessary warning. |
|
139 return false; |
|
140 } |
|
141 |
|
142 MutexAutoLock lock(singleton->mListLock); |
|
143 return (singleton->mActiveSSLSockets > 0); |
|
144 } |
|
145 |
|
146 nsresult nsNSSShutDownList::doPK11Logout() |
|
147 { |
|
148 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("canceling all open SSL sockets to disallow future IO\n")); |
|
149 // During our iteration we will set a bunch of PRBools to true. |
|
150 // Nobody else ever modifies that bool, only we do. |
|
151 // We only must ensure that our objects do not go away. |
|
152 // This is guaranteed by holding the list lock. |
|
153 |
|
154 MutexAutoLock lock(singleton->mListLock); |
|
155 PL_DHashTableEnumerate(&mPK11LogoutCancelObjects, doPK11LogoutHelper, 0); |
|
156 |
|
157 return NS_OK; |
|
158 } |
|
159 |
|
160 PLDHashOperator |
|
161 nsNSSShutDownList::doPK11LogoutHelper(PLDHashTable *table, |
|
162 PLDHashEntryHdr *hdr, uint32_t number, void *arg) |
|
163 { |
|
164 ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr); |
|
165 |
|
166 nsOnPK11LogoutCancelObject *pklco = |
|
167 reinterpret_cast<nsOnPK11LogoutCancelObject*>(entry->obj); |
|
168 |
|
169 if (pklco) { |
|
170 pklco->logout(); |
|
171 } |
|
172 |
|
173 return PL_DHASH_NEXT; |
|
174 } |
|
175 |
|
176 bool nsNSSShutDownList::isUIActive() |
|
177 { |
|
178 bool canDisallow = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::test_only); |
|
179 bool bIsUIActive = !canDisallow; |
|
180 return bIsUIActive; |
|
181 } |
|
182 |
|
183 bool nsNSSShutDownList::ifPossibleDisallowUI() |
|
184 { |
|
185 bool isNowDisallowed = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::do_it_for_real); |
|
186 return isNowDisallowed; |
|
187 } |
|
188 |
|
189 void nsNSSShutDownList::allowUI() |
|
190 { |
|
191 mActivityState.allowUI(); |
|
192 } |
|
193 |
|
194 nsresult nsNSSShutDownList::evaporateAllNSSResources() |
|
195 { |
|
196 if (PR_SUCCESS != mActivityState.restrictActivityToCurrentThread()) { |
|
197 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("failed to restrict activity to current thread\n")); |
|
198 return NS_ERROR_FAILURE; |
|
199 } |
|
200 |
|
201 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("now evaporating NSS resources\n")); |
|
202 int removedCount; |
|
203 do { |
|
204 MutexAutoLock lock(mListLock); |
|
205 removedCount = PL_DHashTableEnumerate(&mObjects, evaporateAllNSSResourcesHelper, 0); |
|
206 } while (removedCount > 0); |
|
207 |
|
208 mActivityState.releaseCurrentThreadActivityRestriction(); |
|
209 return NS_OK; |
|
210 } |
|
211 |
|
212 PLDHashOperator |
|
213 nsNSSShutDownList::evaporateAllNSSResourcesHelper(PLDHashTable *table, |
|
214 PLDHashEntryHdr *hdr, uint32_t number, void *arg) |
|
215 { |
|
216 ObjectHashEntry *entry = static_cast<ObjectHashEntry*>(hdr); |
|
217 { |
|
218 MutexAutoUnlock unlock(singleton->mListLock); |
|
219 entry->obj->shutdown(nsNSSShutDownObject::calledFromList); |
|
220 } |
|
221 // Never free more than one entry, because other threads might be calling |
|
222 // us and remove themselves while we are iterating over the list, |
|
223 // and the behaviour of changing the list while iterating is undefined. |
|
224 return (PLDHashOperator)(PL_DHASH_STOP | PL_DHASH_REMOVE); |
|
225 } |
|
226 |
|
227 nsNSSShutDownList *nsNSSShutDownList::construct() |
|
228 { |
|
229 if (singleton) { |
|
230 // we should never ever be called twice |
|
231 return nullptr; |
|
232 } |
|
233 |
|
234 singleton = new nsNSSShutDownList(); |
|
235 return singleton; |
|
236 } |
|
237 |
|
238 nsNSSActivityState::nsNSSActivityState() |
|
239 :mNSSActivityStateLock("nsNSSActivityState.mNSSActivityStateLock"), |
|
240 mNSSActivityChanged(mNSSActivityStateLock, |
|
241 "nsNSSActivityState.mNSSActivityStateLock"), |
|
242 mNSSActivityCounter(0), |
|
243 mBlockingUICounter(0), |
|
244 mIsUIForbidden(false), |
|
245 mNSSRestrictedThread(nullptr) |
|
246 { |
|
247 } |
|
248 |
|
249 nsNSSActivityState::~nsNSSActivityState() |
|
250 { |
|
251 } |
|
252 |
|
253 void nsNSSActivityState::enter() |
|
254 { |
|
255 MutexAutoLock lock(mNSSActivityStateLock); |
|
256 |
|
257 while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) { |
|
258 mNSSActivityChanged.Wait(); |
|
259 } |
|
260 |
|
261 ++mNSSActivityCounter; |
|
262 } |
|
263 |
|
264 void nsNSSActivityState::leave() |
|
265 { |
|
266 MutexAutoLock lock(mNSSActivityStateLock); |
|
267 |
|
268 --mNSSActivityCounter; |
|
269 |
|
270 mNSSActivityChanged.NotifyAll(); |
|
271 } |
|
272 |
|
273 void nsNSSActivityState::enterBlockingUIState() |
|
274 { |
|
275 MutexAutoLock lock(mNSSActivityStateLock); |
|
276 |
|
277 ++mBlockingUICounter; |
|
278 } |
|
279 |
|
280 void nsNSSActivityState::leaveBlockingUIState() |
|
281 { |
|
282 MutexAutoLock lock(mNSSActivityStateLock); |
|
283 |
|
284 --mBlockingUICounter; |
|
285 } |
|
286 |
|
287 bool nsNSSActivityState::isBlockingUIActive() |
|
288 { |
|
289 MutexAutoLock lock(mNSSActivityStateLock); |
|
290 return (mBlockingUICounter > 0); |
|
291 } |
|
292 |
|
293 bool nsNSSActivityState::isUIForbidden() |
|
294 { |
|
295 MutexAutoLock lock(mNSSActivityStateLock); |
|
296 return mIsUIForbidden; |
|
297 } |
|
298 |
|
299 bool nsNSSActivityState::ifPossibleDisallowUI(RealOrTesting rot) |
|
300 { |
|
301 bool retval = false; |
|
302 MutexAutoLock lock(mNSSActivityStateLock); |
|
303 |
|
304 // Checking and disallowing the UI must be done atomically. |
|
305 |
|
306 if (!mBlockingUICounter) { |
|
307 // No UI is currently shown, we are able to evaporate. |
|
308 retval = true; |
|
309 if (rot == do_it_for_real) { |
|
310 // Remember to disallow UI. |
|
311 mIsUIForbidden = true; |
|
312 |
|
313 // to clear the "forbidden" state, |
|
314 // one must either call |
|
315 // restrictActivityToCurrentThread() + releaseCurrentThreadActivityRestriction() |
|
316 // or cancel the operation by calling |
|
317 // unprepareCurrentThreadRestriction() |
|
318 } |
|
319 } |
|
320 return retval; |
|
321 } |
|
322 |
|
323 void nsNSSActivityState::allowUI() |
|
324 { |
|
325 MutexAutoLock lock(mNSSActivityStateLock); |
|
326 |
|
327 mIsUIForbidden = false; |
|
328 } |
|
329 |
|
330 PRStatus nsNSSActivityState::restrictActivityToCurrentThread() |
|
331 { |
|
332 PRStatus retval = PR_FAILURE; |
|
333 MutexAutoLock lock(mNSSActivityStateLock); |
|
334 |
|
335 if (!mBlockingUICounter) { |
|
336 while (0 < mNSSActivityCounter && !mBlockingUICounter) { |
|
337 mNSSActivityChanged.Wait(PR_TicksPerSecond()); |
|
338 } |
|
339 |
|
340 if (mBlockingUICounter) { |
|
341 // This should never happen. |
|
342 // If we arrive here, our logic is broken. |
|
343 PR_ASSERT(0); |
|
344 } |
|
345 else { |
|
346 mNSSRestrictedThread = PR_GetCurrentThread(); |
|
347 retval = PR_SUCCESS; |
|
348 } |
|
349 } |
|
350 |
|
351 return retval; |
|
352 } |
|
353 |
|
354 void nsNSSActivityState::releaseCurrentThreadActivityRestriction() |
|
355 { |
|
356 MutexAutoLock lock(mNSSActivityStateLock); |
|
357 |
|
358 mNSSRestrictedThread = nullptr; |
|
359 mIsUIForbidden = false; |
|
360 |
|
361 mNSSActivityChanged.NotifyAll(); |
|
362 } |
|
363 |
|
364 nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock() |
|
365 { |
|
366 nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); |
|
367 if (!state) |
|
368 return; |
|
369 |
|
370 state->enter(); |
|
371 } |
|
372 |
|
373 nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock() |
|
374 { |
|
375 nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); |
|
376 if (!state) |
|
377 return; |
|
378 |
|
379 state->leave(); |
|
380 } |
|
381 |
|
382 nsPSMUITracker::nsPSMUITracker() |
|
383 { |
|
384 nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); |
|
385 if (!state) |
|
386 return; |
|
387 |
|
388 state->enterBlockingUIState(); |
|
389 } |
|
390 |
|
391 nsPSMUITracker::~nsPSMUITracker() |
|
392 { |
|
393 nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); |
|
394 if (!state) |
|
395 return; |
|
396 |
|
397 state->leaveBlockingUIState(); |
|
398 } |
|
399 |
|
400 bool nsPSMUITracker::isUIForbidden() |
|
401 { |
|
402 nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); |
|
403 if (!state) |
|
404 return false; |
|
405 |
|
406 return state->isUIForbidden(); |
|
407 } |