security/manager/ssl/src/nsNSSShutDown.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:814cbd61dd2b
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 }

mercurial