Mozilla/mozilla/security/manager/ssl/src/nsNSSShutDown.cpp
brendan%mozilla.org 95220b5330 Add shared DHashTableOps for [const] char *key use-cases, clean up dhash API abusages (214839, r=dougt, sr=dbaron).
git-svn-id: svn://10.0.0.236/trunk@145624 18797224-902f-48f8-a5cc-f745e15eee43
2003-08-05 20:09:21 +00:00

509 lines
13 KiB
C++

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kaie@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsNSSShutDown.h"
#include "nsCOMPtr.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gPIPNSSLog;
#endif
struct ObjectHashEntry : PLDHashEntryHdr {
nsNSSShutDownObject *obj;
};
PR_STATIC_CALLBACK(const void *)
ObjectSetGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
{
ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
return entry->obj;
}
PR_STATIC_CALLBACK(PRBool)
ObjectSetMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const ObjectHashEntry *entry = NS_STATIC_CAST(const ObjectHashEntry*, hdr);
return entry->obj == NS_STATIC_CAST(const nsNSSShutDownObject*, key);
}
PR_STATIC_CALLBACK(PRBool)
ObjectSetInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
const void *key)
{
ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
entry->obj = NS_CONST_CAST(nsNSSShutDownObject*, NS_STATIC_CAST(const nsNSSShutDownObject*, key));
return PR_TRUE;
}
static PLDHashTableOps gSetOps = {
PL_DHashAllocTable,
PL_DHashFreeTable,
ObjectSetGetKey,
PL_DHashVoidPtrKeyStub,
ObjectSetMatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
PL_DHashFinalizeStub,
ObjectSetInitEntry
};
nsNSSShutDownList *nsNSSShutDownList::singleton = nsnull;
nsNSSShutDownList::nsNSSShutDownList()
{
mListLock = PR_NewLock();
mActiveSSLSockets = 0;
mPK11LogoutCancelObjects.ops = nsnull;
mObjects.ops = nsnull;
PL_DHashTableInit(&mObjects, &gSetOps, nsnull,
sizeof(ObjectHashEntry), 16);
PL_DHashTableInit(&mPK11LogoutCancelObjects, &gSetOps, nsnull,
sizeof(ObjectHashEntry), 16);
}
nsNSSShutDownList::~nsNSSShutDownList()
{
if (mListLock) {
PR_DestroyLock(mListLock);
mListLock = nsnull;
}
if (mObjects.ops) {
PL_DHashTableFinish(&mObjects);
mObjects.ops = nsnull;
}
if (mPK11LogoutCancelObjects.ops) {
PL_DHashTableFinish(&mPK11LogoutCancelObjects);
mPK11LogoutCancelObjects.ops = nsnull;
}
PR_ASSERT(this == singleton);
singleton = nsnull;
}
void nsNSSShutDownList::remember(nsNSSShutDownObject *o)
{
if (!singleton)
return;
PR_ASSERT(o);
PR_Lock(singleton->mListLock);
PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_ADD);
PR_Unlock(singleton->mListLock);
}
void nsNSSShutDownList::forget(nsNSSShutDownObject *o)
{
if (!singleton)
return;
PR_ASSERT(o);
PR_Lock(singleton->mListLock);
PL_DHashTableOperate(&singleton->mObjects, o, PL_DHASH_REMOVE);
PR_Unlock(singleton->mListLock);
}
void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o)
{
if (!singleton)
return;
PR_ASSERT(o);
PR_Lock(singleton->mListLock);
PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_ADD);
PR_Unlock(singleton->mListLock);
}
void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o)
{
if (!singleton)
return;
PR_ASSERT(o);
PR_Lock(singleton->mListLock);
PL_DHashTableOperate(&singleton->mPK11LogoutCancelObjects, o, PL_DHASH_REMOVE);
PR_Unlock(singleton->mListLock);
}
void nsNSSShutDownList::trackSSLSocketCreate()
{
if (!singleton)
return;
PR_Lock(singleton->mListLock);
++singleton->mActiveSSLSockets;
PR_Unlock(singleton->mListLock);
}
void nsNSSShutDownList::trackSSLSocketClose()
{
if (!singleton)
return;
PR_Lock(singleton->mListLock);
--singleton->mActiveSSLSockets;
PR_Unlock(singleton->mListLock);
}
PRBool nsNSSShutDownList::areSSLSocketsActive()
{
if (!singleton) {
// I'd rather prefer to be pessimistic and return PR_TRUE.
// However, maybe we will get called at a time when the singleton
// has already been freed, and returning PR_TRUE would bring up an
// unnecessary warning.
return PR_FALSE;
}
PRBool retval;
PR_Lock(singleton->mListLock);
retval = (singleton->mActiveSSLSockets > 0);
PR_Unlock(singleton->mListLock);
return retval;
}
nsresult nsNSSShutDownList::doPK11Logout()
{
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("canceling all open SSL sockets to disallow future IO\n"));
// During our iteration we will set a bunch of PRBools to PR_TRUE.
// Nobody else ever modifies that PRBool, only we do.
// We only must ensure that our objects do not go away.
// This is guaranteed by holding the list lock.
PR_Lock(mListLock);
PL_DHashTableEnumerate(&mPK11LogoutCancelObjects, doPK11LogoutHelper, 0);
PR_Unlock(mListLock);
return NS_OK;
}
PLDHashOperator PR_CALLBACK
nsNSSShutDownList::doPK11LogoutHelper(PLDHashTable *table,
PLDHashEntryHdr *hdr, PRUint32 number, void *arg)
{
ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
nsOnPK11LogoutCancelObject *pklco =
NS_REINTERPRET_CAST(nsOnPK11LogoutCancelObject*, entry->obj);
if (pklco) {
pklco->logout();
}
return PL_DHASH_NEXT;
}
PRBool nsNSSShutDownList::isUIActive()
{
PRBool canDisallow = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::test_only);
PRBool bIsUIActive = !canDisallow;
return bIsUIActive;
}
PRBool nsNSSShutDownList::ifPossibleDisallowUI()
{
PRBool isNowDisallowed = mActivityState.ifPossibleDisallowUI(nsNSSActivityState::do_it_for_real);
return isNowDisallowed;
}
void nsNSSShutDownList::allowUI()
{
mActivityState.allowUI();
}
nsresult nsNSSShutDownList::evaporateAllNSSResources()
{
if (PR_SUCCESS != mActivityState.restrictActivityToCurrentThread()) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("failed to restrict activity to current thread\n"));
return NS_ERROR_FAILURE;
}
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("now evaporating NSS resources\n"));
int removedCount;
do {
PR_Lock(mListLock);
removedCount = PL_DHashTableEnumerate(&mObjects, evaporateAllNSSResourcesHelper, 0);
PR_Unlock(mListLock);
} while (removedCount > 0);
mActivityState.releaseCurrentThreadActivityRestriction();
return NS_OK;
}
PLDHashOperator PR_CALLBACK
nsNSSShutDownList::evaporateAllNSSResourcesHelper(PLDHashTable *table,
PLDHashEntryHdr *hdr, PRUint32 number, void *arg)
{
ObjectHashEntry *entry = NS_STATIC_CAST(ObjectHashEntry*, hdr);
PR_Unlock(singleton->mListLock);
entry->obj->shutdown(nsNSSShutDownObject::calledFromList);
PR_Lock(singleton->mListLock);
// Never free more than one entry, because other threads might be calling
// us and remove themselves while we are iterating over the list,
// and the behaviour of changing the list while iterating is undefined.
return (PLDHashOperator)(PL_DHASH_STOP | PL_DHASH_REMOVE);
}
nsNSSShutDownList *nsNSSShutDownList::construct()
{
if (singleton) {
// we should never ever be called twice
return nsnull;
}
singleton = new nsNSSShutDownList();
return singleton;
}
nsNSSActivityState::nsNSSActivityState()
:mNSSActivityStateLock(nsnull),
mNSSActivityChanged(nsnull),
mNSSActivityCounter(0),
mBlockingUICounter(0),
mIsUIForbidden(PR_FALSE),
mNSSRestrictedThread(nsnull)
{
mNSSActivityStateLock = PR_NewLock();
if (!mNSSActivityStateLock)
return;
mNSSActivityChanged = PR_NewCondVar(mNSSActivityStateLock);
}
nsNSSActivityState::~nsNSSActivityState()
{
if (mNSSActivityChanged) {
PR_DestroyCondVar(mNSSActivityChanged);
mNSSActivityChanged = nsnull;
}
if (mNSSActivityStateLock) {
PR_DestroyLock(mNSSActivityStateLock);
mNSSActivityStateLock = nsnull;
}
}
void nsNSSActivityState::enter()
{
PR_Lock(mNSSActivityStateLock);
while (mNSSRestrictedThread && mNSSRestrictedThread != PR_GetCurrentThread()) {
PR_WaitCondVar(mNSSActivityChanged, PR_INTERVAL_NO_TIMEOUT);
}
++mNSSActivityCounter;
PR_Unlock(mNSSActivityStateLock);
}
void nsNSSActivityState::leave()
{
PR_Lock(mNSSActivityStateLock);
--mNSSActivityCounter;
if (!mNSSActivityCounter) {
PR_NotifyAllCondVar(mNSSActivityChanged);
}
PR_Unlock(mNSSActivityStateLock);
}
void nsNSSActivityState::enterBlockingUIState()
{
PR_Lock(mNSSActivityStateLock);
++mBlockingUICounter;
PR_Unlock(mNSSActivityStateLock);
}
void nsNSSActivityState::leaveBlockingUIState()
{
PR_Lock(mNSSActivityStateLock);
--mBlockingUICounter;
PR_Unlock(mNSSActivityStateLock);
}
PRBool nsNSSActivityState::isBlockingUIActive()
{
PRBool retval;
PR_Lock(mNSSActivityStateLock);
retval = (mBlockingUICounter > 0);
PR_Unlock(mNSSActivityStateLock);
return retval;
}
PRBool nsNSSActivityState::isUIForbidden()
{
PRBool retval;
PR_Lock(mNSSActivityStateLock);
retval = mIsUIForbidden;
PR_Unlock(mNSSActivityStateLock);
return retval;
}
PRBool nsNSSActivityState::ifPossibleDisallowUI(RealOrTesting rot)
{
PRBool retval = PR_FALSE;
PR_Lock(mNSSActivityStateLock);
// Checking and disallowing the UI must be done atomically.
if (!mBlockingUICounter) {
// No UI is currently shown, we are able to evaporate.
retval = PR_TRUE;
if (rot == do_it_for_real) {
// Remember to disallow UI.
mIsUIForbidden = PR_TRUE;
// to clear the "forbidden" state,
// one must either call
// restrictActivityToCurrentThread() + releaseCurrentThreadActivityRestriction()
// or cancel the operation by calling
// unprepareCurrentThreadRestriction()
}
}
PR_Unlock(mNSSActivityStateLock);
return retval;
}
void nsNSSActivityState::allowUI()
{
PR_Lock(mNSSActivityStateLock);
mIsUIForbidden = PR_FALSE;
PR_Unlock(mNSSActivityStateLock);
}
PRStatus nsNSSActivityState::restrictActivityToCurrentThread()
{
PRStatus retval = PR_FAILURE;
PR_Lock(mNSSActivityStateLock);
if (!mBlockingUICounter) {
while (0 < mNSSActivityCounter && !mBlockingUICounter) {
PR_WaitCondVar(mNSSActivityChanged, PR_TicksPerSecond());
}
if (mBlockingUICounter) {
// This should never happen.
// If we arrive here, our logic is broken.
PR_ASSERT(0);
}
else {
mNSSRestrictedThread = PR_GetCurrentThread();
retval = PR_SUCCESS;
}
}
PR_Unlock(mNSSActivityStateLock);
return retval;
}
void nsNSSActivityState::releaseCurrentThreadActivityRestriction()
{
PR_Lock(mNSSActivityStateLock);
mNSSRestrictedThread = nsnull;
mIsUIForbidden = PR_FALSE;
PR_NotifyAllCondVar(mNSSActivityChanged);
PR_Unlock(mNSSActivityStateLock);
}
nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock()
{
nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
if (!state)
return;
state->enter();
}
nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock()
{
nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
if (!state)
return;
state->leave();
}
nsPSMUITracker::nsPSMUITracker()
{
nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
if (!state)
return;
state->enterBlockingUIState();
}
nsPSMUITracker::~nsPSMUITracker()
{
nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
if (!state)
return;
state->leaveBlockingUIState();
}
PRBool nsPSMUITracker::isUIForbidden()
{
nsNSSActivityState *state = nsNSSShutDownList::getActivityState();
if (!state)
return PR_FALSE;
return state->isUIForbidden();
}