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