Back to index

lightning-sunbird  0.9+nobinonly
nsNSSShutDown.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Kai Engert <kaie@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsNSSShutDown.h"
00039 #include "nsCOMPtr.h"
00040 
00041 #ifdef PR_LOGGING
00042 extern PRLogModuleInfo* gPIPNSSLog;
00043 #endif
00044 
00045 struct ObjectHashEntry : PLDHashEntryHdr {
00046   nsNSSShutDownObject *obj;
00047 };
00048 
00049 PR_STATIC_CALLBACK(const void *)
00050 ObjectSetGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00051 {
00052   ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
00053   return entry->obj;
00054 }
00055 
00056 PR_STATIC_CALLBACK(PRBool)
00057 ObjectSetMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
00058                          const void *key)
00059 {
00060   const ObjectHashEntry *entry = NS_STATIC_CAST(const ObjectHashEntry*, hdr);
00061   return entry->obj == NS_STATIC_CAST(const nsNSSShutDownObject*, key);
00062 }
00063 
00064 PR_STATIC_CALLBACK(PRBool)
00065 ObjectSetInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
00066                      const void *key)
00067 {
00068   ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
00069   entry->obj = NS_CONST_CAST(nsNSSShutDownObject*, NS_STATIC_CAST(const nsNSSShutDownObject*, key));
00070   return PR_TRUE;
00071 }
00072 
00073 static PLDHashTableOps gSetOps = {
00074   PL_DHashAllocTable,
00075   PL_DHashFreeTable,
00076   ObjectSetGetKey,
00077   PL_DHashVoidPtrKeyStub,
00078   ObjectSetMatchEntry,
00079   PL_DHashMoveEntryStub,
00080   PL_DHashClearEntryStub,
00081   PL_DHashFinalizeStub,
00082   ObjectSetInitEntry
00083 };
00084 
00085 nsNSSShutDownList *nsNSSShutDownList::singleton = nsnull;
00086 
00087 nsNSSShutDownList::nsNSSShutDownList()
00088 {
00089   mListLock = PR_NewLock();
00090   mActiveSSLSockets = 0;
00091   mPK11LogoutCancelObjects.ops = nsnull;
00092   mObjects.ops = nsnull;
00093   PL_DHashTableInit(&mObjects, &gSetOps, nsnull,
00094                     sizeof(ObjectHashEntry), 16);
00095   PL_DHashTableInit(&mPK11LogoutCancelObjects, &gSetOps, nsnull,
00096                     sizeof(ObjectHashEntry), 16);
00097 }
00098 
00099 nsNSSShutDownList::~nsNSSShutDownList()
00100 {
00101   if (mListLock) {
00102     PR_DestroyLock(mListLock);
00103     mListLock = nsnull;
00104   }
00105   if (mObjects.ops) {
00106     PL_DHashTableFinish(&mObjects);
00107     mObjects.ops = nsnull;
00108   }
00109   if (mPK11LogoutCancelObjects.ops) {
00110     PL_DHashTableFinish(&mPK11LogoutCancelObjects);
00111     mPK11LogoutCancelObjects.ops = nsnull;
00112   }
00113   PR_ASSERT(this == singleton);
00114   singleton = nsnull;
00115 }
00116 
00117 void nsNSSShutDownList::remember(nsNSSShutDownObject *o)
00118 {
00119   if (!singleton)
00120     return;
00121   
00122   PR_ASSERT(o);
00123   PR_Lock(singleton->mListLock);
00124     PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_ADD);
00125   PR_Unlock(singleton->mListLock);
00126 }
00127 
00128 void nsNSSShutDownList::forget(nsNSSShutDownObject *o)
00129 {
00130   if (!singleton)
00131     return;
00132   
00133   PR_ASSERT(o);
00134   PR_Lock(singleton->mListLock);
00135     PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_REMOVE);
00136   PR_Unlock(singleton->mListLock);
00137 }
00138 
00139 void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o)
00140 {
00141   if (!singleton)
00142     return;
00143   
00144   PR_ASSERT(o);
00145   PR_Lock(singleton->mListLock);
00146     PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_ADD);
00147   PR_Unlock(singleton->mListLock);
00148 }
00149 
00150 void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o)
00151 {
00152   if (!singleton)
00153     return;
00154   
00155   PR_ASSERT(o);
00156   PR_Lock(singleton->mListLock);
00157     PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_REMOVE);
00158   PR_Unlock(singleton->mListLock);
00159 }
00160 
00161 void nsNSSShutDownList::trackSSLSocketCreate()
00162 {
00163   if (!singleton)
00164     return;
00165   
00166   PR_Lock(singleton->mListLock);
00167     ++singleton->mActiveSSLSockets;
00168   PR_Unlock(singleton->mListLock);
00169 }
00170 
00171 void nsNSSShutDownList::trackSSLSocketClose()
00172 {
00173   if (!singleton)
00174     return;
00175   
00176   PR_Lock(singleton->mListLock);
00177     --singleton->mActiveSSLSockets;
00178   PR_Unlock(singleton->mListLock);
00179 }
00180   
00181 PRBool nsNSSShutDownList::areSSLSocketsActive()
00182 {
00183   if (!singleton) {
00184     // I'd rather prefer to be pessimistic and return PR_TRUE.
00185     // However, maybe we will get called at a time when the singleton
00186     // has already been freed, and returning PR_TRUE would bring up an 
00187     // unnecessary warning.
00188     return PR_FALSE;
00189   }
00190   
00191   PRBool retval;
00192   PR_Lock(singleton->mListLock);
00193     retval = (singleton->mActiveSSLSockets > 0);
00194   PR_Unlock(singleton->mListLock);
00195 
00196   return retval;
00197 }
00198 
00199 nsresult nsNSSShutDownList::doPK11Logout()
00200 {
00201     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("canceling all open SSL sockets to disallow future IO\n"));
00202   // During our iteration we will set a bunch of PRBools to PR_TRUE.
00203   // Nobody else ever modifies that PRBool, only we do.
00204   // We only must ensure that our objects do not go away.
00205   // This is guaranteed by holding the list lock.
00206 
00207   PR_Lock(mListLock);
00208     PL_DHashTableEnumerate(&mPK11LogoutCancelObjects, doPK11LogoutHelper, 0);
00209   PR_Unlock(mListLock);
00210 
00211   return NS_OK;
00212 }
00213 
00214 PLDHashOperator PR_CALLBACK
00215 nsNSSShutDownList::doPK11LogoutHelper(PLDHashTable *table, 
00216   PLDHashEntryHdr *hdr, PRUint32 number, void *arg)
00217 {
00218   ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
00219 
00220   nsOnPK11LogoutCancelObject *pklco = 
00221     NS_REINTERPRET_CAST(nsOnPK11LogoutCancelObject*, entry->obj);
00222 
00223   if (pklco) {
00224     pklco->logout();
00225   }
00226 
00227   return PL_DHASH_NEXT;
00228 }
00229 
00230 PRBool nsNSSShutDownList::isUIActive()
00231 {
00232   PRBool canDisallow = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::test_only);
00233   PRBool bIsUIActive = !canDisallow;
00234   return bIsUIActive;
00235 }
00236 
00237 PRBool nsNSSShutDownList::ifPossibleDisallowUI()
00238 {
00239   PRBool isNowDisallowed = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::do_it_for_real);
00240   return isNowDisallowed;
00241 }
00242 
00243 void nsNSSShutDownList::allowUI()
00244 {
00245   mActivityState.allowUI();
00246 }
00247 
00248 nsresult nsNSSShutDownList::evaporateAllNSSResources()
00249 {
00250   if (PR_SUCCESS != mActivityState.restrictActivityToCurrentThread()) {
00251     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("failed to restrict activity to current thread\n"));
00252     return NS_ERROR_FAILURE;
00253   }
00254 
00255   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("now evaporating NSS resources\n"));
00256   int removedCount;
00257   do {
00258     PR_Lock(mListLock);
00259       removedCount = PL_DHashTableEnumerate(&mObjects, evaporateAllNSSResourcesHelper, 0);
00260     PR_Unlock(mListLock);
00261   } while (removedCount > 0);
00262 
00263   mActivityState.releaseCurrentThreadActivityRestriction();
00264   return NS_OK;
00265 }
00266 
00267 PLDHashOperator PR_CALLBACK
00268 nsNSSShutDownList::evaporateAllNSSResourcesHelper(PLDHashTable *table, 
00269   PLDHashEntryHdr *hdr, PRUint32 number, void *arg)
00270 {
00271     ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
00272     PR_Unlock(singleton->mListLock);
00273 
00274   entry->obj->shutdown(nsNSSShutDownObject::calledFromList);
00275 
00276     PR_Lock(singleton->mListLock);
00277 
00278     // Never free more than one entry, because other threads might be calling
00279     // us and remove themselves while we are iterating over the list,
00280     // and the behaviour of changing the list while iterating is undefined.
00281     return (PLDHashOperator)(PL_DHASH_STOP | PL_DHASH_REMOVE);
00282 }
00283 
00284 nsNSSShutDownList *nsNSSShutDownList::construct()
00285 {
00286   if (singleton) {
00287     // we should never ever be called twice
00288     return nsnull;
00289   }
00290 
00291   singleton = new nsNSSShutDownList();
00292   return singleton;
00293 }
00294 
00295 nsNSSActivityState::nsNSSActivityState()
00296 :mNSSActivityStateLock(nsnull), 
00297  mNSSActivityChanged(nsnull),
00298  mNSSActivityCounter(0),
00299  mBlockingUICounter(0),
00300  mIsUIForbidden(PR_FALSE),
00301  mNSSRestrictedThread(nsnull)
00302 {
00303   mNSSActivityStateLock = PR_NewLock();
00304   if (!mNSSActivityStateLock)
00305     return;
00306 
00307   mNSSActivityChanged = PR_NewCondVar(mNSSActivityStateLock);
00308 }
00309 
00310 nsNSSActivityState::~nsNSSActivityState()
00311 {
00312   if (mNSSActivityChanged) {
00313     PR_DestroyCondVar(mNSSActivityChanged);
00314     mNSSActivityChanged = nsnull;
00315   }
00316 
00317   if (mNSSActivityStateLock) {
00318     PR_DestroyLock(mNSSActivityStateLock);
00319     mNSSActivityStateLock = nsnull;
00320   }
00321 }
00322 
00323 void nsNSSActivityState::enter()
00324 {
00325   PR_Lock(mNSSActivityStateLock);
00326 
00327     while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) {
00328       PR_WaitCondVar(mNSSActivityChanged, PR_INTERVAL_NO_TIMEOUT);
00329     }
00330 
00331     ++mNSSActivityCounter;
00332   
00333   PR_Unlock(mNSSActivityStateLock);
00334 }
00335 
00336 void nsNSSActivityState::leave()
00337 {
00338   PR_Lock(mNSSActivityStateLock);
00339 
00340     --mNSSActivityCounter;
00341 
00342     if (!mNSSActivityCounter) {
00343       PR_NotifyAllCondVar(mNSSActivityChanged);
00344     }
00345 
00346   PR_Unlock(mNSSActivityStateLock);
00347 }
00348 
00349 void nsNSSActivityState::enterBlockingUIState()
00350 {
00351   PR_Lock(mNSSActivityStateLock);
00352 
00353     ++mBlockingUICounter;
00354 
00355   PR_Unlock(mNSSActivityStateLock);
00356 }
00357 
00358 void nsNSSActivityState::leaveBlockingUIState()
00359 {
00360   PR_Lock(mNSSActivityStateLock);
00361 
00362     --mBlockingUICounter;
00363 
00364   PR_Unlock(mNSSActivityStateLock);
00365 }
00366 
00367 PRBool nsNSSActivityState::isBlockingUIActive()
00368 {
00369   PRBool retval;
00370 
00371   PR_Lock(mNSSActivityStateLock);
00372     retval = (mBlockingUICounter > 0);
00373   PR_Unlock(mNSSActivityStateLock);
00374 
00375   return retval;
00376 }
00377 
00378 PRBool nsNSSActivityState::isUIForbidden()
00379 {
00380   PRBool retval;
00381   
00382   PR_Lock(mNSSActivityStateLock);
00383     retval = mIsUIForbidden;
00384   PR_Unlock(mNSSActivityStateLock);
00385 
00386   return retval;
00387 }
00388 
00389 PRBool nsNSSActivityState::ifPossibleDisallowUI(RealOrTesting rot)
00390 {
00391   PRBool retval = PR_FALSE;
00392 
00393   PR_Lock(mNSSActivityStateLock);
00394 
00395     // Checking and disallowing the UI must be done atomically.
00396 
00397     if (!mBlockingUICounter) {
00398       // No UI is currently shown, we are able to evaporate.
00399       retval = PR_TRUE;
00400       if (rot == do_it_for_real) {
00401         // Remember to disallow UI.
00402         mIsUIForbidden = PR_TRUE;
00403         
00404         // to clear the "forbidden" state,
00405         // one must either call 
00406         // restrictActivityToCurrentThread() + releaseCurrentThreadActivityRestriction()
00407         // or cancel the operation by calling
00408         // unprepareCurrentThreadRestriction()
00409       }
00410     }
00411   
00412   PR_Unlock(mNSSActivityStateLock);
00413 
00414   return retval;
00415 }
00416 
00417 void nsNSSActivityState::allowUI()
00418 {
00419   PR_Lock(mNSSActivityStateLock);
00420 
00421     mIsUIForbidden = PR_FALSE;
00422   
00423   PR_Unlock(mNSSActivityStateLock);
00424 }
00425 
00426 PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
00427 {
00428   PRStatus retval = PR_FAILURE;
00429 
00430   PR_Lock(mNSSActivityStateLock);
00431   
00432     if (!mBlockingUICounter) {
00433       while (0 < mNSSActivityCounter && !mBlockingUICounter) {
00434         PR_WaitCondVar(mNSSActivityChanged, PR_TicksPerSecond());
00435       }
00436       
00437       if (mBlockingUICounter) {
00438         // This should never happen.
00439         // If we arrive here, our logic is broken.
00440         PR_ASSERT(0);
00441       }
00442       else {
00443         mNSSRestrictedThread = PR_GetCurrentThread();
00444         retval = PR_SUCCESS;
00445       }
00446     }
00447   
00448   PR_Unlock(mNSSActivityStateLock);
00449 
00450   return retval;
00451 }
00452 
00453 void nsNSSActivityState::releaseCurrentThreadActivityRestriction()
00454 {
00455   PR_Lock(mNSSActivityStateLock);
00456 
00457     mNSSRestrictedThread = nsnull;
00458     mIsUIForbidden = PR_FALSE;
00459 
00460     PR_NotifyAllCondVar(mNSSActivityChanged);
00461 
00462   PR_Unlock(mNSSActivityStateLock);
00463 }
00464 
00465 nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock()
00466 {
00467   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
00468   if (!state)
00469     return;
00470 
00471   state->enter();
00472 }
00473 
00474 nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock()
00475 {
00476   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
00477   if (!state)
00478     return;
00479   
00480   state->leave();
00481 }
00482 
00483 nsPSMUITracker::nsPSMUITracker()
00484 {
00485   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
00486   if (!state)
00487     return;
00488   
00489   state->enterBlockingUIState();
00490 }
00491 
00492 nsPSMUITracker::~nsPSMUITracker()
00493 {
00494   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
00495   if (!state)
00496     return;
00497   
00498   state->leaveBlockingUIState();
00499 }
00500 
00501 PRBool nsPSMUITracker::isUIForbidden()
00502 {
00503   nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
00504   if (!state)
00505     return PR_FALSE;
00506 
00507   return state->isUIForbidden();
00508 }