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