mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
1146 lines
28 KiB
C
1146 lines
28 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 the Netscape security libraries.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* 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 ***** */
|
|
|
|
#ifdef DEBUG
|
|
static const char CVS_ID[] = "@(#) $RCSfile: arena.c,v $ $Revision: 1.7 $ $Date: 2004/04/25 15:03:02 $ $Name: $";
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* arena.c
|
|
*
|
|
* This contains the implementation of NSS's thread-safe arenas.
|
|
*/
|
|
|
|
#ifndef BASE_H
|
|
#include "base.h"
|
|
#endif /* BASE_H */
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
#include "prthread.h"
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
#include "prlock.h"
|
|
#include "plarena.h"
|
|
|
|
#include <string.h>
|
|
|
|
/*
|
|
* NSSArena
|
|
*
|
|
* This is based on NSPR's arena code, but it is threadsafe.
|
|
*
|
|
* The public methods relating to this type are:
|
|
*
|
|
* NSSArena_Create -- constructor
|
|
* NSSArena_Destroy
|
|
*
|
|
* The nonpublic methods relating to this type are:
|
|
*
|
|
* nssArena_Create -- constructor
|
|
* nssArena_Destroy
|
|
* nssArena_Mark
|
|
* nssArena_Release
|
|
* nssArena_Unmark
|
|
*
|
|
* nss_ZAlloc
|
|
* nss_ZFreeIf
|
|
* nss_ZRealloc
|
|
*
|
|
* In debug builds, the following calls are available:
|
|
*
|
|
* nssArena_verifyPointer
|
|
* nssArena_registerDestructor
|
|
* nssArena_deregisterDestructor
|
|
*/
|
|
|
|
struct NSSArenaStr {
|
|
PLArenaPool pool;
|
|
PRLock *lock;
|
|
#ifdef ARENA_THREADMARK
|
|
PRThread *marking_thread;
|
|
nssArenaMark *first_mark;
|
|
nssArenaMark *last_mark;
|
|
#endif /* ARENA_THREADMARK */
|
|
#ifdef ARENA_DESTRUCTOR_LIST
|
|
struct arena_destructor_node *first_destructor;
|
|
struct arena_destructor_node *last_destructor;
|
|
#endif /* ARENA_DESTRUCTOR_LIST */
|
|
};
|
|
|
|
/*
|
|
* nssArenaMark
|
|
*
|
|
* This type is used to mark the current state of an NSSArena.
|
|
*/
|
|
|
|
struct nssArenaMarkStr {
|
|
PRUint32 magic;
|
|
void *mark;
|
|
#ifdef ARENA_THREADMARK
|
|
nssArenaMark *next;
|
|
#endif /* ARENA_THREADMARK */
|
|
#ifdef ARENA_DESTRUCTOR_LIST
|
|
struct arena_destructor_node *next_destructor;
|
|
struct arena_destructor_node *prev_destructor;
|
|
#endif /* ARENA_DESTRUCTOR_LIST */
|
|
};
|
|
|
|
#define MARK_MAGIC 0x4d41524b /* "MARK" how original */
|
|
|
|
/*
|
|
* But first, the pointer-tracking code
|
|
*/
|
|
#ifdef DEBUG
|
|
extern const NSSError NSS_ERROR_INTERNAL_ERROR;
|
|
|
|
static nssPointerTracker arena_pointer_tracker;
|
|
|
|
static PRStatus
|
|
arena_add_pointer
|
|
(
|
|
const NSSArena *arena
|
|
)
|
|
{
|
|
PRStatus rv;
|
|
|
|
rv = nssPointerTracker_initialize(&arena_pointer_tracker);
|
|
if( PR_SUCCESS != rv ) {
|
|
return rv;
|
|
}
|
|
|
|
rv = nssPointerTracker_add(&arena_pointer_tracker, arena);
|
|
if( PR_SUCCESS != rv ) {
|
|
NSSError e = NSS_GetError();
|
|
if( NSS_ERROR_NO_MEMORY != e ) {
|
|
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static PRStatus
|
|
arena_remove_pointer
|
|
(
|
|
const NSSArena *arena
|
|
)
|
|
{
|
|
PRStatus rv;
|
|
|
|
rv = nssPointerTracker_remove(&arena_pointer_tracker, arena);
|
|
if( PR_SUCCESS != rv ) {
|
|
nss_SetError(NSS_ERROR_INTERNAL_ERROR);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* nssArena_verifyPointer
|
|
*
|
|
* This method is only present in debug builds.
|
|
*
|
|
* If the specified pointer is a valid pointer to an NSSArena object,
|
|
* this routine will return PR_SUCCESS. Otherwise, it will put an
|
|
* error on the error stack and return PR_FAILURE.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS if the pointer is valid
|
|
* PR_FAILURE if it isn't
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssArena_verifyPointer
|
|
(
|
|
const NSSArena *arena
|
|
)
|
|
{
|
|
PRStatus rv;
|
|
|
|
rv = nssPointerTracker_initialize(&arena_pointer_tracker);
|
|
if( PR_SUCCESS != rv ) {
|
|
/*
|
|
* This is a little disingenious. We have to initialize the
|
|
* tracker, because someone could "legitimately" try to verify
|
|
* an arena pointer before one is ever created. And this step
|
|
* might fail, due to lack of memory. But the only way that
|
|
* this step can fail is if it's doing the call_once stuff,
|
|
* (later calls just no-op). And if it didn't no-op, there
|
|
* aren't any valid arenas.. so the argument certainly isn't one.
|
|
*/
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
rv = nssPointerTracker_verify(&arena_pointer_tracker, arena);
|
|
if( PR_SUCCESS != rv ) {
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
return PR_SUCCESS;
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
#ifdef ARENA_DESTRUCTOR_LIST
|
|
|
|
struct arena_destructor_node {
|
|
struct arena_destructor_node *next;
|
|
struct arena_destructor_node *prev;
|
|
void (*destructor)(void *argument);
|
|
void *arg;
|
|
};
|
|
|
|
/*
|
|
* nssArena_registerDestructor
|
|
*
|
|
* This routine stores a pointer to a callback and an arbitrary
|
|
* pointer-sized argument in the arena, at the current point in
|
|
* the mark stack. If the arena is destroyed, or an "earlier"
|
|
* mark is released, then this destructor will be called at that
|
|
* time. Note that the destructor will be called with the arena
|
|
* locked, which means the destructor may free memory in that
|
|
* arena, but it may not allocate or cause to be allocated any
|
|
* memory. This callback facility was included to support our
|
|
* debug-version pointer-tracker feature; overuse runs counter to
|
|
* the the original intent of arenas. This routine returns a
|
|
* PRStatus value; if successful, it will return PR_SUCCESS. If
|
|
* unsuccessful, it will set an error on the error stack and
|
|
* return PR_FAILURE.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
* NSS_ERROR_NO_MEMORY
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS
|
|
* PR_FAILURE
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssArena_registerDestructor
|
|
(
|
|
NSSArena *arena,
|
|
void (*destructor)(void *argument),
|
|
void *arg
|
|
)
|
|
{
|
|
struct arena_destructor_node *it;
|
|
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
it = nss_ZNEW(arena, struct arena_destructor_node);
|
|
if( (struct arena_destructor_node *)NULL == it ) {
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
it->prev = arena->last_destructor;
|
|
arena->last_destructor->next = it;
|
|
arena->last_destructor = it;
|
|
it->destructor = destructor;
|
|
it->arg = arg;
|
|
|
|
if( (nssArenaMark *)NULL != arena->last_mark ) {
|
|
arena->last_mark->prev_destructor = it->prev;
|
|
arena->last_mark->next_destructor = it->next;
|
|
}
|
|
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssArena_deregisterDestructor
|
|
(
|
|
NSSArena *arena,
|
|
void (*destructor)(void *argument),
|
|
void *arg
|
|
)
|
|
{
|
|
struct arena_destructor_node *it;
|
|
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
for( it = arena->first_destructor; it; it = it->next ) {
|
|
if( (it->destructor == destructor) && (it->arg == arg) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( (struct arena_destructor_node *)NULL == it ) {
|
|
nss_SetError(NSS_ERROR_NOT_FOUND);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
if( it == arena->first_destructor ) {
|
|
arena->first_destructor = it->next;
|
|
}
|
|
|
|
if( it == arena->last_destructor ) {
|
|
arena->last_destructor = it->prev;
|
|
}
|
|
|
|
if( (struct arena_destructor_node *)NULL != it->prev ) {
|
|
it->prev->next = it->next;
|
|
}
|
|
|
|
if( (struct arena_destructor_node *)NULL != it->next ) {
|
|
it->next->prev = it->prev;
|
|
}
|
|
|
|
{
|
|
nssArenaMark *m;
|
|
for( m = arena->first_mark; m; m = m->next ) {
|
|
if( m->next_destructor == it ) {
|
|
m->next_destructor = it->next;
|
|
}
|
|
if( m->prev_destructor == it ) {
|
|
m->prev_destructor = it->prev;
|
|
}
|
|
}
|
|
}
|
|
|
|
nss_ZFreeIf(it);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
nss_arena_call_destructor_chain
|
|
(
|
|
struct arena_destructor_node *it
|
|
)
|
|
{
|
|
for( ; it ; it = it->next ) {
|
|
(*(it->destructor))(it->arg);
|
|
}
|
|
}
|
|
|
|
#endif /* ARENA_DESTRUCTOR_LIST */
|
|
|
|
/*
|
|
* NSSArena_Create
|
|
*
|
|
* This routine creates a new memory arena. This routine may return
|
|
* NULL upon error, in which case it will have created an error stack.
|
|
*
|
|
* The top-level error may be one of the following values:
|
|
* NSS_ERROR_NO_MEMORY
|
|
*
|
|
* Return value:
|
|
* NULL upon error
|
|
* A pointer to an NSSArena upon success
|
|
*/
|
|
|
|
NSS_IMPLEMENT NSSArena *
|
|
NSSArena_Create
|
|
(
|
|
void
|
|
)
|
|
{
|
|
nss_ClearErrorStack();
|
|
return nssArena_Create();
|
|
}
|
|
|
|
/*
|
|
* nssArena_Create
|
|
*
|
|
* This routine creates a new memory arena. This routine may return
|
|
* NULL upon error, in which case it will have set an error on the
|
|
* error stack.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_NO_MEMORY
|
|
*
|
|
* Return value:
|
|
* NULL upon error
|
|
* A pointer to an NSSArena upon success
|
|
*/
|
|
|
|
NSS_IMPLEMENT NSSArena *
|
|
nssArena_Create
|
|
(
|
|
void
|
|
)
|
|
{
|
|
NSSArena *rv = (NSSArena *)NULL;
|
|
|
|
rv = nss_ZNEW((NSSArena *)NULL, NSSArena);
|
|
if( (NSSArena *)NULL == rv ) {
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (NSSArena *)NULL;
|
|
}
|
|
|
|
rv->lock = PR_NewLock();
|
|
if( (PRLock *)NULL == rv->lock ) {
|
|
(void)nss_ZFreeIf(rv);
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (NSSArena *)NULL;
|
|
}
|
|
|
|
/*
|
|
* Arena sizes. The current security code has 229 occurrences of
|
|
* PORT_NewArena. The default chunksizes specified break down as
|
|
*
|
|
* Size Mult. Specified as
|
|
* 512 1 512
|
|
* 1024 7 1024
|
|
* 2048 5 2048
|
|
* 2048 5 CRMF_DEFAULT_ARENA_SIZE
|
|
* 2048 190 DER_DEFAULT_CHUNKSIZE
|
|
* 2048 20 SEC_ASN1_DEFAULT_ARENA_SIZE
|
|
* 4096 1 4096
|
|
*
|
|
* Obviously this "default chunksize" flexibility isn't very
|
|
* useful to us, so I'll just pick 2048.
|
|
*/
|
|
|
|
PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double));
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
PRStatus st;
|
|
st = arena_add_pointer(rv);
|
|
if( PR_SUCCESS != st ) {
|
|
PL_FinishArenaPool(&rv->pool);
|
|
PR_DestroyLock(rv->lock);
|
|
(void)nss_ZFreeIf(rv);
|
|
return (NSSArena *)NULL;
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* NSSArena_Destroy
|
|
*
|
|
* This routine will destroy the specified arena, freeing all memory
|
|
* allocated from it. This routine returns a PRStatus value; if
|
|
* successful, it will return PR_SUCCESS. If unsuccessful, it will
|
|
* create an error stack and return PR_FAILURE.
|
|
*
|
|
* The top-level error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS upon success
|
|
* PR_FAILURE upon failure
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
NSSArena_Destroy
|
|
(
|
|
NSSArena *arena
|
|
)
|
|
{
|
|
nss_ClearErrorStack();
|
|
|
|
#ifdef DEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
return nssArena_Destroy(arena);
|
|
}
|
|
|
|
/*
|
|
* nssArena_Destroy
|
|
*
|
|
* This routine will destroy the specified arena, freeing all memory
|
|
* allocated from it. This routine returns a PRStatus value; if
|
|
* successful, it will return PR_SUCCESS. If unsuccessful, it will
|
|
* set an error on the error stack and return PR_FAILURE.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS
|
|
* PR_FAILURE
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssArena_Destroy
|
|
(
|
|
NSSArena *arena
|
|
)
|
|
{
|
|
PRLock *lock;
|
|
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
PR_Lock(arena->lock);
|
|
if( (PRLock *)NULL == arena->lock ) {
|
|
/* Just got destroyed */
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if( PR_SUCCESS != arena_remove_pointer(arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
#ifdef ARENA_DESTRUCTOR_LIST
|
|
/* Note that the arena is locked at this time */
|
|
nss_arena_call_destructor_chain(arena->first_destructor);
|
|
#endif /* ARENA_DESTRUCTOR_LIST */
|
|
|
|
PL_FinishArenaPool(&arena->pool);
|
|
lock = arena->lock;
|
|
arena->lock = (PRLock *)NULL;
|
|
PR_Unlock(lock);
|
|
PR_DestroyLock(lock);
|
|
(void)nss_ZFreeIf(arena);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size);
|
|
|
|
/*
|
|
* nssArena_Mark
|
|
*
|
|
* This routine "marks" the current state of an arena. Space
|
|
* allocated after the arena has been marked can be freed by
|
|
* releasing the arena back to the mark with nssArena_Release,
|
|
* or committed by calling nssArena_Unmark. When successful,
|
|
* this routine returns a valid nssArenaMark pointer. This
|
|
* routine may return NULL upon error, in which case it will
|
|
* have set an error on the error stack.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
* NSS_ERROR_NO_MEMORY
|
|
* NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
|
|
*
|
|
* Return value:
|
|
* NULL upon failure
|
|
* An nssArenaMark pointer upon success
|
|
*/
|
|
|
|
NSS_IMPLEMENT nssArenaMark *
|
|
nssArena_Mark
|
|
(
|
|
NSSArena *arena
|
|
)
|
|
{
|
|
nssArenaMark *rv;
|
|
void *p;
|
|
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
|
|
return (nssArenaMark *)NULL;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
PR_Lock(arena->lock);
|
|
if( (PRLock *)NULL == arena->lock ) {
|
|
/* Just got destroyed */
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA);
|
|
return (nssArenaMark *)NULL;
|
|
}
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
if( (PRThread *)NULL == arena->marking_thread ) {
|
|
/* Unmarked. Store our thread ID */
|
|
arena->marking_thread = PR_GetCurrentThread();
|
|
/* This call never fails. */
|
|
} else {
|
|
/* Marked. Verify it's the current thread */
|
|
if( PR_GetCurrentThread() != arena->marking_thread ) {
|
|
PR_Unlock(arena->lock);
|
|
nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
|
|
return (nssArenaMark *)NULL;
|
|
}
|
|
}
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
p = PL_ARENA_MARK(&arena->pool);
|
|
/* No error possible */
|
|
|
|
/* Do this after the mark */
|
|
rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark));
|
|
if( (nssArenaMark *)NULL == rv ) {
|
|
PR_Unlock(arena->lock);
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (nssArenaMark *)NULL;
|
|
}
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
if ( (nssArenaMark *)NULL == arena->first_mark) {
|
|
arena->first_mark = rv;
|
|
arena->last_mark = rv;
|
|
} else {
|
|
arena->last_mark->next = rv;
|
|
arena->last_mark = rv;
|
|
}
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
rv->mark = p;
|
|
rv->magic = MARK_MAGIC;
|
|
|
|
#ifdef ARENA_DESTRUCTOR_LIST
|
|
rv->prev_destructor = arena->last_destructor;
|
|
#endif /* ARENA_DESTRUCTOR_LIST */
|
|
|
|
PR_Unlock(arena->lock);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* nss_arena_unmark_release
|
|
*
|
|
* This static routine implements the routines nssArena_Release
|
|
* ans nssArena_Unmark, which are almost identical.
|
|
*/
|
|
|
|
static PRStatus
|
|
nss_arena_unmark_release
|
|
(
|
|
NSSArena *arena,
|
|
nssArenaMark *arenaMark,
|
|
PRBool release
|
|
)
|
|
{
|
|
void *inner_mark;
|
|
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
if( MARK_MAGIC != arenaMark->magic ) {
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
PR_Lock(arena->lock);
|
|
if( (PRLock *)NULL == arena->lock ) {
|
|
/* Just got destroyed */
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
if( (PRThread *)NULL != arena->marking_thread ) {
|
|
if( PR_GetCurrentThread() != arena->marking_thread ) {
|
|
PR_Unlock(arena->lock);
|
|
nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
|
|
return PR_FAILURE;
|
|
}
|
|
}
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
if( MARK_MAGIC != arenaMark->magic ) {
|
|
/* Just got released */
|
|
PR_Unlock(arena->lock);
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
arenaMark->magic = 0;
|
|
inner_mark = arenaMark->mark;
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
{
|
|
nssArenaMark **pMark = &arena->first_mark;
|
|
nssArenaMark *rest;
|
|
nssArenaMark *last = (nssArenaMark *)NULL;
|
|
|
|
/* Find this mark */
|
|
while( *pMark != arenaMark ) {
|
|
last = *pMark;
|
|
pMark = &(*pMark)->next;
|
|
}
|
|
|
|
/* Remember the pointer, then zero it */
|
|
rest = (*pMark)->next;
|
|
*pMark = (nssArenaMark *)NULL;
|
|
|
|
arena->last_mark = last;
|
|
|
|
/* Invalidate any later marks being implicitly released */
|
|
for( ; (nssArenaMark *)NULL != rest; rest = rest->next ) {
|
|
rest->magic = 0;
|
|
}
|
|
|
|
/* If we just got rid of the first mark, clear the thread ID */
|
|
if( (nssArenaMark *)NULL == arena->first_mark ) {
|
|
arena->marking_thread = (PRThread *)NULL;
|
|
}
|
|
}
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
if( release ) {
|
|
#ifdef ARENA_DESTRUCTOR_LIST
|
|
if( (struct arena_destructor_node *)NULL != arenaMark->prev_destructor ) {
|
|
arenaMark->prev_destructor->next = (struct arena_destructor_node *)NULL;
|
|
}
|
|
arena->last_destructor = arenaMark->prev_destructor;
|
|
|
|
/* Note that the arena is locked at this time */
|
|
nss_arena_call_destructor_chain(arenaMark->next_destructor);
|
|
#endif /* ARENA_DESTRUCTOR_LIST */
|
|
|
|
PR_ARENA_RELEASE(&arena->pool, inner_mark);
|
|
/* No error return */
|
|
}
|
|
|
|
PR_Unlock(arena->lock);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* nssArena_Release
|
|
*
|
|
* This routine invalidates and releases all memory allocated from
|
|
* the specified arena after the point at which the specified mark
|
|
* was obtained. This routine returns a PRStatus value; if successful,
|
|
* it will return PR_SUCCESS. If unsuccessful, it will set an error
|
|
* on the error stack and return PR_FAILURE.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
* NSS_ERROR_INVALID_ARENA_MARK
|
|
* NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS
|
|
* PR_FAILURE
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssArena_Release
|
|
(
|
|
NSSArena *arena,
|
|
nssArenaMark *arenaMark
|
|
)
|
|
{
|
|
return nss_arena_unmark_release(arena, arenaMark, PR_TRUE);
|
|
}
|
|
|
|
/*
|
|
* nssArena_Unmark
|
|
*
|
|
* This routine "commits" the indicated mark and any marks after
|
|
* it, making them unreleasable. Note that any earlier marks can
|
|
* still be released, and such a release will invalidate these
|
|
* later unmarked regions. If an arena is to be safely shared by
|
|
* more than one thread, all marks must be either released or
|
|
* unmarked. This routine returns a PRStatus value; if successful,
|
|
* it will return PR_SUCCESS. If unsuccessful, it will set an error
|
|
* on the error stack and return PR_FAILURE.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
* NSS_ERROR_INVALID_ARENA_MARK
|
|
* NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS
|
|
* PR_FAILURE
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nssArena_Unmark
|
|
(
|
|
NSSArena *arena,
|
|
nssArenaMark *arenaMark
|
|
)
|
|
{
|
|
return nss_arena_unmark_release(arena, arenaMark, PR_FALSE);
|
|
}
|
|
|
|
/*
|
|
* We prefix this header to all allocated blocks. It is a multiple
|
|
* of the alignment size. Note that this usage of a header may make
|
|
* purify spew bogus warnings about "potentially leaked blocks" of
|
|
* memory; if that gets too annoying we can add in a pointer to the
|
|
* header in the header itself. There's not a lot of safety here;
|
|
* maybe we should add a magic value?
|
|
*/
|
|
struct pointer_header {
|
|
NSSArena *arena;
|
|
PRUint32 size;
|
|
};
|
|
|
|
static void *
|
|
nss_zalloc_arena_locked
|
|
(
|
|
NSSArena *arena,
|
|
PRUint32 size
|
|
)
|
|
{
|
|
void *p;
|
|
void *rv;
|
|
struct pointer_header *h;
|
|
PRUint32 my_size = size + sizeof(struct pointer_header);
|
|
PR_ARENA_ALLOCATE(p, &arena->pool, my_size);
|
|
if( (void *)NULL == p ) {
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (void *)NULL;
|
|
}
|
|
/*
|
|
* Do this before we unlock. This way if the user is using
|
|
* an arena in one thread while destroying it in another, he'll
|
|
* fault/FMR in his code, not ours.
|
|
*/
|
|
h = (struct pointer_header *)p;
|
|
h->arena = arena;
|
|
h->size = size;
|
|
rv = (void *)((char *)h + sizeof(struct pointer_header));
|
|
(void)nsslibc_memset(rv, 0, size);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* nss_ZAlloc
|
|
*
|
|
* This routine allocates and zeroes a section of memory of the
|
|
* size, and returns to the caller a pointer to that memory. If
|
|
* the optional arena argument is non-null, the memory will be
|
|
* obtained from that arena; otherwise, the memory will be obtained
|
|
* from the heap. This routine may return NULL upon error, in
|
|
* which case it will have set an error upon the error stack. The
|
|
* value specified for size may be zero; in which case a valid
|
|
* zero-length block of memory will be allocated. This block may
|
|
* be expanded by calling nss_ZRealloc.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_ARENA
|
|
* NSS_ERROR_NO_MEMORY
|
|
* NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
|
|
*
|
|
* Return value:
|
|
* NULL upon error
|
|
* A pointer to the new segment of zeroed memory
|
|
*/
|
|
|
|
NSS_IMPLEMENT void *
|
|
nss_ZAlloc
|
|
(
|
|
NSSArena *arenaOpt,
|
|
PRUint32 size
|
|
)
|
|
{
|
|
struct pointer_header *h;
|
|
PRUint32 my_size = size + sizeof(struct pointer_header);
|
|
|
|
if( my_size < sizeof(struct pointer_header) ) {
|
|
/* Wrapped */
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
if( (NSSArena *)NULL == arenaOpt ) {
|
|
/* Heap allocation, no locking required. */
|
|
h = (struct pointer_header *)PR_Calloc(1, my_size);
|
|
if( (struct pointer_header *)NULL == h ) {
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
h->arena = (NSSArena *)NULL;
|
|
h->size = size;
|
|
/* We used calloc: it's already zeroed */
|
|
|
|
return (void *)((char *)h + sizeof(struct pointer_header));
|
|
} else {
|
|
void *rv;
|
|
/* Arena allocation */
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
|
|
return (void *)NULL;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
PR_Lock(arenaOpt->lock);
|
|
if( (PRLock *)NULL == arenaOpt->lock ) {
|
|
/* Just got destroyed */
|
|
nss_SetError(NSS_ERROR_INVALID_ARENA);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
if( (PRThread *)NULL != arenaOpt->marking_thread ) {
|
|
if( PR_GetCurrentThread() != arenaOpt->marking_thread ) {
|
|
nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
|
|
PR_Unlock(arenaOpt->lock);
|
|
return (void *)NULL;
|
|
}
|
|
}
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
rv = nss_zalloc_arena_locked(arenaOpt, size);
|
|
|
|
PR_Unlock(arenaOpt->lock);
|
|
return rv;
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* nss_ZFreeIf
|
|
*
|
|
* If the specified pointer is non-null, then the region of memory
|
|
* to which it points -- which must have been allocated with
|
|
* nss_ZAlloc -- will be zeroed and released. This routine
|
|
* returns a PRStatus value; if successful, it will return PR_SUCCESS.
|
|
* If unsuccessful, it will set an error on the error stack and return
|
|
* PR_FAILURE.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_POINTER
|
|
*
|
|
* Return value:
|
|
* PR_SUCCESS
|
|
* PR_FAILURE
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
nss_ZFreeIf
|
|
(
|
|
void *pointer
|
|
)
|
|
{
|
|
struct pointer_header *h;
|
|
|
|
if( (void *)NULL == pointer ) {
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
h = (struct pointer_header *)((char *)pointer
|
|
- sizeof(struct pointer_header));
|
|
|
|
/* Check any magic here */
|
|
|
|
if( (NSSArena *)NULL == h->arena ) {
|
|
/* Heap */
|
|
(void)nsslibc_memset(pointer, 0, h->size);
|
|
PR_Free(h);
|
|
return PR_SUCCESS;
|
|
} else {
|
|
/* Arena */
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) {
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
PR_Lock(h->arena->lock);
|
|
if( (PRLock *)NULL == h->arena->lock ) {
|
|
/* Just got destroyed.. so this pointer is invalid */
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
(void)nsslibc_memset(pointer, 0, h->size);
|
|
|
|
/* No way to "free" it within an NSPR arena. */
|
|
|
|
PR_Unlock(h->arena->lock);
|
|
return PR_SUCCESS;
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* nss_ZRealloc
|
|
*
|
|
* This routine reallocates a block of memory obtained by calling
|
|
* nss_ZAlloc or nss_ZRealloc. The portion of memory
|
|
* between the new and old sizes -- which is either being newly
|
|
* obtained or released -- is in either case zeroed. This routine
|
|
* may return NULL upon failure, in which case it will have placed
|
|
* an error on the error stack.
|
|
*
|
|
* The error may be one of the following values:
|
|
* NSS_ERROR_INVALID_POINTER
|
|
* NSS_ERROR_NO_MEMORY
|
|
* NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
|
|
*
|
|
* Return value:
|
|
* NULL upon error
|
|
* A pointer to the replacement segment of memory
|
|
*/
|
|
|
|
NSS_EXTERN void *
|
|
nss_ZRealloc
|
|
(
|
|
void *pointer,
|
|
PRUint32 newSize
|
|
)
|
|
{
|
|
struct pointer_header *h, *new_h;
|
|
PRUint32 my_newSize = newSize + sizeof(struct pointer_header);
|
|
void *rv;
|
|
|
|
if( my_newSize < sizeof(struct pointer_header) ) {
|
|
/* Wrapped */
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
if( (void *)NULL == pointer ) {
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
h = (struct pointer_header *)((char *)pointer
|
|
- sizeof(struct pointer_header));
|
|
|
|
/* Check any magic here */
|
|
|
|
if( newSize == h->size ) {
|
|
/* saves thrashing */
|
|
return pointer;
|
|
}
|
|
|
|
if( (NSSArena *)NULL == h->arena ) {
|
|
/* Heap */
|
|
new_h = (struct pointer_header *)PR_Calloc(1, my_newSize);
|
|
if( (struct pointer_header *)NULL == new_h ) {
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
new_h->arena = (NSSArena *)NULL;
|
|
new_h->size = newSize;
|
|
rv = (void *)((char *)new_h + sizeof(struct pointer_header));
|
|
|
|
if( newSize > h->size ) {
|
|
(void)nsslibc_memcpy(rv, pointer, h->size);
|
|
(void)nsslibc_memset(&((char *)rv)[ h->size ],
|
|
0, (newSize - h->size));
|
|
} else {
|
|
(void)nsslibc_memcpy(rv, pointer, newSize);
|
|
}
|
|
|
|
(void)nsslibc_memset(pointer, 0, h->size);
|
|
h->size = 0;
|
|
PR_Free(h);
|
|
|
|
return rv;
|
|
} else {
|
|
void *p;
|
|
/* Arena */
|
|
#ifdef NSSDEBUG
|
|
if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) {
|
|
return (void *)NULL;
|
|
}
|
|
#endif /* NSSDEBUG */
|
|
|
|
PR_Lock(h->arena->lock);
|
|
if( (PRLock *)NULL == h->arena->lock ) {
|
|
/* Just got destroyed.. so this pointer is invalid */
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
#ifdef ARENA_THREADMARK
|
|
if( (PRThread *)NULL != h->arena->marking_thread ) {
|
|
if( PR_GetCurrentThread() != h->arena->marking_thread ) {
|
|
PR_Unlock(h->arena->lock);
|
|
nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
|
|
return (void *)NULL;
|
|
}
|
|
}
|
|
#endif /* ARENA_THREADMARK */
|
|
|
|
if( newSize < h->size ) {
|
|
/*
|
|
* We have no general way of returning memory to the arena
|
|
* (mark/release doesn't work because things may have been
|
|
* allocated after this object), so the memory is gone
|
|
* anyway. We might as well just return the same pointer to
|
|
* the user, saying "yeah, uh-hunh, you can only use less of
|
|
* it now." We'll zero the leftover part, of course. And
|
|
* in fact we might as well *not* adjust h->size-- this way,
|
|
* if the user reallocs back up to something not greater than
|
|
* the original size, then voila, there's the memory! This
|
|
* way a thrash big/small/big/small doesn't burn up the arena.
|
|
*/
|
|
char *extra = &((char *)pointer)[ newSize ];
|
|
(void)nsslibc_memset(extra, 0, (h->size - newSize));
|
|
PR_Unlock(h->arena->lock);
|
|
return pointer;
|
|
}
|
|
|
|
PR_ARENA_ALLOCATE(p, &h->arena->pool, my_newSize);
|
|
if( (void *)NULL == p ) {
|
|
PR_Unlock(h->arena->lock);
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
return (void *)NULL;
|
|
}
|
|
|
|
new_h = (struct pointer_header *)p;
|
|
new_h->arena = h->arena;
|
|
new_h->size = newSize;
|
|
rv = (void *)((char *)new_h + sizeof(struct pointer_header));
|
|
if (rv != pointer) {
|
|
(void)nsslibc_memcpy(rv, pointer, h->size);
|
|
(void)nsslibc_memset(pointer, 0, h->size);
|
|
}
|
|
(void)nsslibc_memset(&((char *)rv)[ h->size ], 0, (newSize - h->size));
|
|
h->arena = (NSSArena *)NULL;
|
|
h->size = 0;
|
|
PR_Unlock(new_h->arena->lock);
|
|
return rv;
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|