Mercurial > trustbridge > nss-cmake-static
diff nss/lib/base/arena.c @ 0:1e5118fa0cb1
This is NSS with a Cmake Buildsyste
To compile a static NSS library for Windows we've used the
Chromium-NSS fork and added a Cmake buildsystem to compile
it statically for Windows. See README.chromium for chromium
changes and README.trustbridge for our modifications.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 28 Jul 2014 10:47:06 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nss/lib/base/arena.c Mon Jul 28 10:47:06 2014 +0200 @@ -0,0 +1,1214 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * 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 + * NSS_ZAlloc + * NSS_ZRealloc + * NSS_ZFreeIf + * + * 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 */ + + if( (PRLock *)NULL == arena->lock ) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + PR_Lock(arena->lock); + +#ifdef DEBUG + if( PR_SUCCESS != arena_remove_pointer(arena) ) { + PR_Unlock(arena->lock); + 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 */ + + if( (PRLock *)NULL == arena->lock ) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return (nssArenaMark *)NULL; + } + PR_Lock(arena->lock); + +#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; + } + + if( (PRLock *)NULL == arena->lock ) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + PR_Lock(arena->lock); + +#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 */ + + PL_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); + PL_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 +) +{ + return nss_ZAlloc(arenaOpt, size); +} + +/* + * 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 */ + + if( (PRLock *)NULL == arenaOpt->lock ) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return (void *)NULL; + } + PR_Lock(arenaOpt->lock); + +#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 +) +{ + return nss_ZFreeIf(pointer); +} + +/* + * 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 */ + + if( (PRLock *)NULL == h->arena->lock ) { + /* Just got destroyed.. so this pointer is invalid */ + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + PR_Lock(h->arena->lock); + + (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 +) +{ + return nss_ZRealloc(pointer, newSize); +} + +/* + * 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 +) +{ + NSSArena *arena; + 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; + } + + arena = h->arena; + if (!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(arena)) { + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + if (!arena->lock) { + /* Just got destroyed.. so this pointer is invalid */ + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if (arena->marking_thread) { + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(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(arena->lock); + return pointer; + } + + PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize); + if( (void *)NULL == p ) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + new_h = (struct pointer_header *)p; + new_h->arena = 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(arena->lock); + return rv; + } + /*NOTREACHED*/ +} + +PRStatus +nssArena_Shutdown(void) +{ + PRStatus rv = PR_SUCCESS; +#ifdef DEBUG + rv = nssPointerTracker_finalize(&arena_pointer_tracker); +#endif + return rv; +}