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