security/manager/ssl/src/nsNSSShutDown.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial