Fix for 102251 . Implement 2-level locking for the SSL session cache to properly support SSL server applications using Windows NT fibers . Also optimize and enhance portability of locking primitives for single-process servers on all platforms by using a PRLock instead of cross-process locks . Reviewed by wtc

This commit is contained in:
jpierre%netscape.com 2001-10-06 00:14:33 +00:00
Родитель 6912d44952
Коммит b163eeba5a
3 изменённых файлов: 342 добавлений и 67 удалений

Просмотреть файл

@ -30,12 +30,59 @@
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: sslmutex.c,v 1.3 2001/06/12 22:53:00 nelsonb%netscape.com Exp $
* $Id: sslmutex.c,v 1.4 2001/10/06 00:14:33 jpierre%netscape.com Exp $
*/
#include "sslmutex.h"
#include "prerr.h"
static SECStatus single_process_sslMutex_Init(sslMutex* pMutex)
{
PR_ASSERT(pMutex != 0 && pMutex->u.sslLock == 0 );
pMutex->u.sslLock = PR_NewLock();
if (!pMutex->u.sslLock) {
return SECFailure;
}
return SECSuccess;
}
static SECStatus single_process_sslMutex_Destroy(sslMutex* pMutex)
{
PR_ASSERT(pMutex != 0);
PR_ASSERT(pMutex->u.sslLock!= 0);
if (!pMutex->u.sslLock) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
PR_DestroyLock(pMutex->u.sslLock);
return SECSuccess;
}
static SECStatus single_process_sslMutex_Unlock(sslMutex* pMutex)
{
PR_ASSERT(pMutex != 0 );
PR_ASSERT(pMutex->u.sslLock !=0);
if (!pMutex->u.sslLock) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
PR_Unlock(pMutex->u.sslLock);
return SECSuccess;
}
static SECStatus single_process_sslMutex_Lock(sslMutex* pMutex)
{
PR_ASSERT(pMutex != 0);
PR_ASSERT(pMutex->u.sslLock != 0 );
if (!pMutex->u.sslLock) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
PR_Lock(pMutex->u.sslLock);
return SECSuccess;
}
#if defined(LINUX) || defined(AIX)
#include <unistd.h>
@ -76,34 +123,38 @@ SECStatus
sslMutex_Init(sslMutex *pMutex, int shared)
{
int err;
PR_ASSERT(pMutex);
pMutex->isMultiProcess = (PRBool)(shared != 0);
if (!shared) {
return single_process_sslMutex_Init(pMutex);
}
pMutex->u.pipeStr.mPipes[0] = -1;
pMutex->u.pipeStr.mPipes[1] = -1;
pMutex->u.pipeStr.mPipes[2] = -1;
pMutex->u.pipeStr.nWaiters = 0;
pMutex->mPipes[0] = -1;
pMutex->mPipes[1] = -1;
pMutex->mPipes[2] = -1;
pMutex->nWaiters = 0;
err = pipe(pMutex->mPipes);
err = pipe(pMutex->u.pipeStr.mPipes);
if (err) {
return err;
}
/* close-on-exec is false by default */
if (!shared) {
err = fcntl(pMutex->mPipes[0], F_SETFD, FD_CLOEXEC);
err = fcntl(pMutex->u.pipeStr.mPipes[0], F_SETFD, FD_CLOEXEC);
if (err)
goto loser;
err = fcntl(pMutex->mPipes[1], F_SETFD, FD_CLOEXEC);
err = fcntl(pMutex->u.pipeStr.mPipes[1], F_SETFD, FD_CLOEXEC);
if (err)
goto loser;
}
#if NONBLOCKING_POSTS
err = setNonBlocking(pMutex->mPipes[1], 1);
err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
if (err)
goto loser;
#endif
pMutex->mPipes[2] = SSL_MUTEX_MAGIC;
pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
#if defined(LINUX) && defined(i386)
/* Pipe starts out empty */
@ -115,25 +166,28 @@ sslMutex_Init(sslMutex *pMutex, int shared)
loser:
nss_MD_unix_map_default_error(errno);
close(pMutex->mPipes[0]);
close(pMutex->mPipes[1]);
close(pMutex->u.pipeStr.mPipes[0]);
close(pMutex->u.pipeStr.mPipes[1]);
return SECFailure;
}
SECStatus
sslMutex_Destroy(sslMutex *pMutex)
{
if (pMutex->mPipes[2] != SSL_MUTEX_MAGIC) {
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Destroy(pMutex);
}
if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
close(pMutex->mPipes[0]);
close(pMutex->mPipes[1]);
close(pMutex->u.pipeStr.mPipes[0]);
close(pMutex->u.pipeStr.mPipes[1]);
pMutex->mPipes[0] = -1;
pMutex->mPipes[1] = -1;
pMutex->mPipes[2] = -1;
pMutex->nWaiters = 0;
pMutex->u.pipeStr.mPipes[0] = -1;
pMutex->u.pipeStr.mPipes[1] = -1;
pMutex->u.pipeStr.mPipes[2] = -1;
pMutex->u.pipeStr.nWaiters = 0;
return SECSuccess;
}
@ -145,6 +199,9 @@ SECStatus
sslMutex_Unlock(sslMutex *pMutex)
{
PRInt32 oldValue;
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Unlock(pMutex);
}
if (pMutex->mPipes[2] != SSL_MUTEX_MAGIC) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
@ -173,6 +230,9 @@ SECStatus
sslMutex_Lock(sslMutex *pMutex)
{
PRInt32 oldValue;
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Lock(pMutex);
}
if (pMutex->mPipes[2] != SSL_MUTEX_MAGIC) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
@ -211,12 +271,16 @@ sslMutex_Unlock(sslMutex *pMutex)
int cc;
char c = 1;
if (pMutex->mPipes[2] != SSL_MUTEX_MAGIC) {
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Unlock(pMutex);
}
if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
do {
cc = write(pMutex->mPipes[1], &c, 1);
cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
} while (cc < 0 && (errno == EINTR || errno == EAGAIN));
if (cc != 1) {
if (cc < 0)
@ -235,13 +299,17 @@ sslMutex_Lock(sslMutex *pMutex)
int cc;
char c;
if (pMutex->mPipes[2] != SSL_MUTEX_MAGIC) {
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Lock(pMutex);
}
if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
do {
cc = read(pMutex->mPipes[0], &c, 1);
cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
} while (cc < 0 && errno == EINTR);
if (cc != 1) {
if (cc < 0)
@ -260,22 +328,69 @@ sslMutex_Lock(sslMutex *pMutex)
#include "win32err.h"
/* The presence of the TRUE element in this struct makes the semaphore
* inheritable. The NULL means use process's default security descriptor.
*/
/* on Windows, we need to find the optimal type of locking mechanism to use
for the sslMutex.
There are 3 cases :
1) single-process, use a PRLock, as for all other platforms
2) Win95 multi-process, use a Win32 mutex
3) on WINNT multi-process, use a PRLock + a Win32 mutex
*/
#ifdef WINNT
SECStatus sslMutex_2LevelInit(sslMutex *sem)
{
/* the following adds a PRLock to sslMutex . This is done in each
process of a multi-process server and is only needed on WINNT, if
using fibers. We can't tell if native threads or fibers are used, so
we always do it on WINNT
*/
PR_ASSERT(sem);
if (sem) {
/* we need to reset the sslLock in the children or the 2Level init
function below will assert */
sem->u.sslLock = NULL;
}
return single_process_sslMutex_Init(sem);
}
static SECStatus sslMutex_2LevelDestroy(sslMutex *sem)
{
return single_process_sslMutex_Destroy(sem);
}
#endif
SECStatus
sslMutex_Init(sslMutex *pMutex, int shared)
{
SECStatus retvalue;
HANDLE hMutex;
SECURITY_ATTRIBUTES attributes =
{ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
PR_ASSERT(pMutex != 0 && (*pMutex == 0 || *pMutex == INVALID_HANDLE_VALUE));
if (!pMutex || ((hMutex = *pMutex) != 0 && hMutex != INVALID_HANDLE_VALUE)) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
PR_ASSERT(pMutex != 0 && (pMutex->u.sslMutx == 0 ||
pMutex->u.sslMutx == INVALID_HANDLE_VALUE) );
pMutex->isMultiProcess = (PRBool)(shared != 0);
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Init(pMutex);
}
#ifdef WINNT
/* we need a lock on WINNT for fibers in the parent process */
retvalue = sslMutex_2LevelInit(pMutex);
if (SECSuccess != retvalue)
return SECFailure;
#endif
if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 &&
hMutex != INVALID_HANDLE_VALUE)) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
attributes.bInheritHandle = (shared ? TRUE : FALSE);
hMutex = CreateMutex(&attributes, FALSE, NULL);
@ -284,28 +399,44 @@ sslMutex_Init(sslMutex *pMutex, int shared)
nss_MD_win32_map_default_error(GetLastError());
return SECFailure;
}
*pMutex = hMutex;
pMutex->u.sslMutx = hMutex;
return SECSuccess;
}
int
SECStatus
sslMutex_Destroy(sslMutex *pMutex)
{
HANDLE hMutex;
int rv;
int retvalue = SECSuccess;
PR_ASSERT(pMutex != 0 && *pMutex != 0 && *pMutex != INVALID_HANDLE_VALUE);
if (!pMutex || (hMutex = *pMutex) == 0 || hMutex == INVALID_HANDLE_VALUE) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
PR_ASSERT(pMutex != 0);
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Destroy(pMutex);
}
/* multi-process mode */
#ifdef WINNT
/* on NT, get rid of the PRLock used for fibers within a process */
retvalue = sslMutex_2LevelDestroy(pMutex);
#endif
PR_ASSERT( pMutex->u.sslMutx != 0 &&
pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0
|| hMutex == INVALID_HANDLE_VALUE) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
rv = CloseHandle(hMutex); /* ignore error */
if (rv) {
*pMutex = hMutex = INVALID_HANDLE_VALUE;
return SECSuccess;
pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE;
} else {
nss_MD_win32_map_default_error(GetLastError());
retvalue = SECFailure;
}
nss_MD_win32_map_default_error(GetLastError());
return SECFailure;
return retvalue;
}
int
@ -314,17 +445,29 @@ sslMutex_Unlock(sslMutex *pMutex)
BOOL success = FALSE;
HANDLE hMutex;
PR_ASSERT(pMutex != 0 && *pMutex != 0 && *pMutex != INVALID_HANDLE_VALUE);
if (!pMutex || (hMutex = *pMutex) == 0 || hMutex == INVALID_HANDLE_VALUE) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
PR_ASSERT(pMutex != 0 );
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Unlock(pMutex);
}
PR_ASSERT(pMutex->u.sslMutx != 0 &&
pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 ||
hMutex == INVALID_HANDLE_VALUE) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
success = ReleaseMutex(hMutex);
if (!success) {
nss_MD_win32_map_default_error(GetLastError());
return SECFailure;
}
#ifdef WINNT
return single_process_sslMutex_Unlock(pMutex);
/* release PRLock for other fibers in the process */
#else
return SECSuccess;
#endif
}
int
@ -334,12 +477,25 @@ sslMutex_Lock(sslMutex *pMutex)
DWORD event;
DWORD lastError;
SECStatus rv;
SECStatus retvalue = SECSuccess;
PR_ASSERT(pMutex != 0);
PR_ASSERT(pMutex != 0 && *pMutex != 0 && *pMutex != INVALID_HANDLE_VALUE);
if (!pMutex || (hMutex = *pMutex) == 0 || hMutex == INVALID_HANDLE_VALUE) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Lock(pMutex);
}
#ifdef WINNT
/* lock first to preserve from other threads/fibers
in the same process */
retvalue = single_process_sslMutex_Lock(pMutex);
#endif
PR_ASSERT(pMutex->u.sslMutx != 0 &&
pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 ||
hMutex == INVALID_HANDLE_VALUE) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure; /* what else ? */
}
/* acquire the mutex to be the only owner accross all other processes */
event = WaitForSingleObject(hMutex, INFINITE);
switch (event) {
case WAIT_OBJECT_0:
@ -361,7 +517,12 @@ sslMutex_Lock(sslMutex *pMutex)
nss_MD_win32_map_default_error(lastError);
break;
}
return rv;
if (! (SECSuccess == retvalue && SECSuccess == rv)) {
return SECFailure;
}
return SECSuccess;
}
#elif defined(XP_UNIX)
@ -373,12 +534,17 @@ SECStatus
sslMutex_Init(sslMutex *pMutex, int shared)
{
int rv;
PR_ASSERT(pMutex);
pMutex->isMultiProcess = (PRBool)(shared != 0);
if (!shared) {
return single_process_sslMutex_Init(pMutex);
}
do {
rv = sem_init(pMutex, shared, 1);
rv = sem_init(&pMutex->u.sem, shared, 1);
} while (rv < 0 && errno == EINTR);
if (rv < 0) {
nss_MD_unix_map_default_error(errno);
return SECFailure;
nss_MD_unix_map_default_error(errno);
return SECFailure;
}
return SECSuccess;
}
@ -387,8 +553,11 @@ SECStatus
sslMutex_Destroy(sslMutex *pMutex)
{
int rv;
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Destroy(pMutex);
}
do {
rv = sem_destroy(pMutex);
rv = sem_destroy(&pMutex->u.sem);
} while (rv < 0 && errno == EINTR);
if (rv < 0) {
nss_MD_unix_map_default_error(errno);
@ -401,8 +570,11 @@ SECStatus
sslMutex_Unlock(sslMutex *pMutex)
{
int rv;
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Unlock(pMutex);
}
do {
rv = sem_post(pMutex);
rv = sem_post(&pMutex->u.sem);
} while (rv < 0 && errno == EINTR);
if (rv < 0) {
nss_MD_unix_map_default_error(errno);
@ -415,8 +587,11 @@ SECStatus
sslMutex_Lock(sslMutex *pMutex)
{
int rv;
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Lock(pMutex);
}
do {
rv = sem_wait(pMutex);
rv = sem_wait(&pMutex->u.sem);
} while (rv < 0 && errno == EINTR);
if (rv < 0) {
nss_MD_unix_map_default_error(errno);
@ -430,7 +605,12 @@ sslMutex_Lock(sslMutex *pMutex)
SECStatus
sslMutex_Init(sslMutex *pMutex, int shared)
{
PORT_Assert(!("sslMutex_Init not implemented!"));
PR_ASSERT(pMutex);
pMutex->isMultiProcess = (PRBool)(shared != 0);
if (!shared) {
return single_process_sslMutex_Init(pMutex);
}
PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !"));
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}
@ -438,7 +618,11 @@ sslMutex_Init(sslMutex *pMutex, int shared)
SECStatus
sslMutex_Destroy(sslMutex *pMutex)
{
PORT_Assert(!("sslMutex_Destroy not implemented!"));
PR_ASSERT(pMutex);
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Destroy(pMutex);
}
PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !"));
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}
@ -446,7 +630,11 @@ sslMutex_Destroy(sslMutex *pMutex)
SECStatus
sslMutex_Unlock(sslMutex *pMutex)
{
PORT_Assert(!("sslMutex_Unlock not implemented!"));
PR_ASSERT(pMutex);
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Unlock(pMutex);
}
PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !"));
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}
@ -454,7 +642,11 @@ sslMutex_Unlock(sslMutex *pMutex)
SECStatus
sslMutex_Lock(sslMutex *pMutex)
{
PORT_Assert(!("sslMutex_Lock not implemented!"));
PR_ASSERT(pMutex);
if (PR_FALSE == pMutex->isMultiProcess) {
return single_process_sslMutex_Lock(pMutex);
}
PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !"));
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}

Просмотреть файл

@ -30,7 +30,7 @@
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: sslmutex.h,v 1.2 2001/06/12 01:10:01 nelsonb%netscape.com Exp $
* $Id: sslmutex.h,v 1.3 2001/10/06 00:14:33 jpierre%netscape.com Exp $
*/
#ifndef __SSLMUTEX_H_
#define __SSLMUTEX_H_ 1
@ -54,10 +54,27 @@
* So, this API looks a lot like POSIX pthread mutexes.
*/
#include "prtypes.h"
#include "prlock.h"
#if defined(WIN32)
#include <wtypes.h>
typedef HANDLE sslMutex;
typedef struct
{
PRBool isMultiProcess;
#ifdef WINNT
/* on WINNT we need both the PRLock and the Win32 mutex for fibers */
struct {
#else
union {
#endif
PRLock* sslLock;
HANDLE sslMutx;
} u;
} sslMutex;
typedef int sslPID;
#elif defined(LINUX) || defined(AIX)
@ -66,8 +83,14 @@ typedef int sslPID;
#include "prtypes.h"
typedef struct {
int mPipes[3];
PRInt32 nWaiters;
PRBool isMultiProcess;
union {
PRLock* sslLock;
struct {
int mPipes[3];
PRInt32 nWaiters;
} pipeStr;
} u;
} sslMutex;
typedef pid_t sslPID;
@ -76,14 +99,29 @@ typedef pid_t sslPID;
#include <sys/types.h> /* for pid_t */
#include <semaphore.h> /* for sem_t, and sem_* functions */
typedef sem_t sslMutex;
typedef struct
{
PRBool isMultiProcess;
union {
PRLock* sslLock;
sem_t sem;
} u;
} sslMutex;
typedef pid_t sslPID;
#else
/* what platform is this ?? */
typedef int sslMutex;
typedef struct {
PRBool isMultiProcess;
union {
PRLock* sslLock;
/* include cross-process locking mechanism here */
} u;
} sslMutex;
typedef int sslPID;
#endif
@ -100,6 +138,12 @@ extern SECStatus sslMutex_Unlock(sslMutex *sem);
extern SECStatus sslMutex_Lock(sslMutex *sem);
#ifdef WINNT
extern SECStatus sslMutex_2LevelInit(sslMutex *sem);
#endif
SEC_END_PROTOS
#endif

Просмотреть файл

@ -32,7 +32,7 @@
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: sslsnce.c,v 1.14 2001/09/18 01:59:20 nelsonb%netscape.com Exp $
* $Id: sslsnce.c,v 1.15 2001/10/06 00:14:33 jpierre%netscape.com Exp $
*/
/* Note: ssl_FreeSID() in sslnonce.c gets used for both client and server
@ -1180,6 +1180,11 @@ SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
ptrdiff_t ptr;
inheritance inherit;
cacheDesc my;
#ifdef WINNT
sidCacheLock* newLocks;
int locks_initialized = 0;
int locks_to_initialize = 0;
#endif
myPid = SSL_GETPID();
@ -1261,6 +1266,40 @@ SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
*(ptrdiff_t *)(&cache->certCacheData) += ptr;
*(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
#ifdef WINNT
/* On Windows NT we need to "fix" the sidCacheLocks here to support fibers
When NT fibers are used in a multi-process server, a second level of
locking is needed to prevent a deadlock, in case a fiber acquires the
cross-process mutex, yields, and another fiber is later scheduled on
the same native thread and tries to acquire the cross-process mutex.
We do this by using a PRLock in the sslMutex. However, it is stored in
shared memory as part of sidCacheLocks, and we don't want to overwrite
the PRLock of the parent process. So we need to make new, private
copies of sidCacheLocks before modifying the sslMutex with our own
PRLock
*/
newLocks = (sidCacheLock*)PORT_Alloc(sizeof(sidCacheLock)*(cache->numSIDCacheLocks + 2));
/* note from jpierre : this should be free'd in child processes when
a function is added to delete the SSL session cache in the future */
/* fix the locks */
for (locks_to_initialize = cache->numSIDCacheLocks + 2;
locks_initialized < locks_to_initialize;
++locks_initialized) {
/* copy the old lock */
memcpy(&newLocks[locks_initialized], &cache->sidCacheLocks[locks_initialized], sizeof(sidCacheLock));
/* now, make a local PRLock in this sslMutex for this child process */
sslMutex_2LevelInit(&newLocks[locks_initialized].mutex);
}
/* then, make our cache object point to our new private sidCacheLocks */
/* first the session cache */
cache->sidCacheLocks = newLocks;
/* also fix the key and cert cache which use the last 2 lock entries */
cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
cache->certCacheLock = cache->keyCacheLock + 1;
#endif
PORT_Free(decoString);
isMultiProcess = PR_TRUE;
return SECSuccess;