Back to index

lightning-sunbird  0.9+nobinonly
arena.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #ifdef DEBUG
00038 static const char CVS_ID[] = "@(#) $RCSfile: arena.c,v $ $Revision: 1.8.28.1 $ $Date: 2006/07/17 21:50:53 $";
00039 #endif /* DEBUG */
00040 
00041 /*
00042  * arena.c
00043  *
00044  * This contains the implementation of NSS's thread-safe arenas.
00045  */
00046 
00047 #ifndef BASE_H
00048 #include "base.h"
00049 #endif /* BASE_H */
00050 
00051 #ifdef ARENA_THREADMARK
00052 #include "prthread.h"
00053 #endif /* ARENA_THREADMARK */
00054 
00055 #include "prlock.h"
00056 #include "plarena.h"
00057 
00058 #include <string.h>
00059 
00060 /*
00061  * NSSArena
00062  *
00063  * This is based on NSPR's arena code, but it is threadsafe.
00064  *
00065  * The public methods relating to this type are:
00066  *
00067  *  NSSArena_Create  -- constructor
00068  *  NSSArena_Destroy
00069  *
00070  * The nonpublic methods relating to this type are:
00071  *
00072  *  nssArena_Create  -- constructor
00073  *  nssArena_Destroy
00074  *  nssArena_Mark
00075  *  nssArena_Release
00076  *  nssArena_Unmark
00077  * 
00078  *  nss_ZAlloc
00079  *  nss_ZFreeIf
00080  *  nss_ZRealloc
00081  *
00082  * In debug builds, the following calls are available:
00083  *
00084  *  nssArena_verifyPointer
00085  *  nssArena_registerDestructor
00086  *  nssArena_deregisterDestructor
00087  */
00088 
00089 struct NSSArenaStr {
00090   PLArenaPool pool;
00091   PRLock *lock;
00092 #ifdef ARENA_THREADMARK
00093   PRThread *marking_thread;
00094   nssArenaMark *first_mark;
00095   nssArenaMark *last_mark;
00096 #endif /* ARENA_THREADMARK */
00097 #ifdef ARENA_DESTRUCTOR_LIST
00098   struct arena_destructor_node *first_destructor;
00099   struct arena_destructor_node *last_destructor;
00100 #endif /* ARENA_DESTRUCTOR_LIST */
00101 };
00102 
00103 /*
00104  * nssArenaMark
00105  *
00106  * This type is used to mark the current state of an NSSArena.
00107  */
00108 
00109 struct nssArenaMarkStr {
00110   PRUint32 magic;
00111   void *mark;
00112 #ifdef ARENA_THREADMARK
00113   nssArenaMark *next;
00114 #endif /* ARENA_THREADMARK */
00115 #ifdef ARENA_DESTRUCTOR_LIST
00116   struct arena_destructor_node *next_destructor;
00117   struct arena_destructor_node *prev_destructor;
00118 #endif /* ARENA_DESTRUCTOR_LIST */
00119 };
00120 
00121 #define MARK_MAGIC 0x4d41524b /* "MARK" how original */
00122 
00123 /*
00124  * But first, the pointer-tracking code
00125  */
00126 #ifdef DEBUG
00127 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
00128 
00129 static nssPointerTracker arena_pointer_tracker;
00130 
00131 static PRStatus
00132 arena_add_pointer
00133 (
00134   const NSSArena *arena
00135 )
00136 {
00137   PRStatus rv;
00138 
00139   rv = nssPointerTracker_initialize(&arena_pointer_tracker);
00140   if( PR_SUCCESS != rv ) {
00141     return rv;
00142   }
00143 
00144   rv = nssPointerTracker_add(&arena_pointer_tracker, arena);
00145   if( PR_SUCCESS != rv ) {
00146     NSSError e = NSS_GetError();
00147     if( NSS_ERROR_NO_MEMORY != e ) {
00148       nss_SetError(NSS_ERROR_INTERNAL_ERROR);
00149     }
00150 
00151     return rv;
00152   }
00153 
00154   return PR_SUCCESS;
00155 }
00156 
00157 static PRStatus
00158 arena_remove_pointer
00159 (
00160   const NSSArena *arena
00161 )
00162 {
00163   PRStatus rv;
00164 
00165   rv = nssPointerTracker_remove(&arena_pointer_tracker, arena);
00166   if( PR_SUCCESS != rv ) {
00167     nss_SetError(NSS_ERROR_INTERNAL_ERROR);
00168   }
00169 
00170   return rv;
00171 }
00172 
00173 /*
00174  * nssArena_verifyPointer
00175  *
00176  * This method is only present in debug builds.
00177  *
00178  * If the specified pointer is a valid pointer to an NSSArena object,
00179  * this routine will return PR_SUCCESS.  Otherwise, it will put an
00180  * error on the error stack and return PR_FAILURE.
00181  *
00182  * The error may be one of the following values:
00183  *  NSS_ERROR_INVALID_ARENA
00184  *
00185  * Return value:
00186  *  PR_SUCCESS if the pointer is valid
00187  *  PR_FAILURE if it isn't
00188  */
00189 
00190 NSS_IMPLEMENT PRStatus
00191 nssArena_verifyPointer
00192 (
00193   const NSSArena *arena
00194 )
00195 {
00196   PRStatus rv;
00197 
00198   rv = nssPointerTracker_initialize(&arena_pointer_tracker);
00199   if( PR_SUCCESS != rv ) {
00200     /*
00201      * This is a little disingenious.  We have to initialize the
00202      * tracker, because someone could "legitimately" try to verify
00203      * an arena pointer before one is ever created.  And this step
00204      * might fail, due to lack of memory.  But the only way that
00205      * this step can fail is if it's doing the call_once stuff,
00206      * (later calls just no-op).  And if it didn't no-op, there
00207      * aren't any valid arenas.. so the argument certainly isn't one.
00208      */
00209     nss_SetError(NSS_ERROR_INVALID_ARENA);
00210     return PR_FAILURE;
00211   }
00212 
00213   rv = nssPointerTracker_verify(&arena_pointer_tracker, arena);
00214   if( PR_SUCCESS != rv ) {
00215     nss_SetError(NSS_ERROR_INVALID_ARENA);
00216     return PR_FAILURE;
00217   }
00218 
00219   return PR_SUCCESS;
00220 }
00221 #endif /* DEBUG */
00222 
00223 #ifdef ARENA_DESTRUCTOR_LIST
00224 
00225 struct arena_destructor_node {
00226   struct arena_destructor_node *next;
00227   struct arena_destructor_node *prev;
00228   void (*destructor)(void *argument);
00229   void *arg;
00230 };
00231 
00232 /*
00233  * nssArena_registerDestructor
00234  *
00235  * This routine stores a pointer to a callback and an arbitrary
00236  * pointer-sized argument in the arena, at the current point in
00237  * the mark stack.  If the arena is destroyed, or an "earlier"
00238  * mark is released, then this destructor will be called at that
00239  * time.  Note that the destructor will be called with the arena
00240  * locked, which means the destructor may free memory in that
00241  * arena, but it may not allocate or cause to be allocated any
00242  * memory.  This callback facility was included to support our
00243  * debug-version pointer-tracker feature; overuse runs counter to
00244  * the the original intent of arenas.  This routine returns a 
00245  * PRStatus value; if successful, it will return PR_SUCCESS.  If 
00246  * unsuccessful, it will set an error on the error stack and 
00247  * return PR_FAILURE.
00248  *
00249  * The error may be one of the following values:
00250  *  NSS_ERROR_INVALID_ARENA
00251  *  NSS_ERROR_NO_MEMORY
00252  *
00253  * Return value:
00254  *  PR_SUCCESS
00255  *  PR_FAILURE
00256  */
00257 
00258 NSS_IMPLEMENT PRStatus
00259 nssArena_registerDestructor
00260 (
00261   NSSArena *arena,
00262   void (*destructor)(void *argument),
00263   void *arg
00264 )
00265 {
00266   struct arena_destructor_node *it;
00267 
00268 #ifdef NSSDEBUG
00269   if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
00270     return PR_FAILURE;
00271   }
00272 #endif /* NSSDEBUG */
00273   
00274   it = nss_ZNEW(arena, struct arena_destructor_node);
00275   if( (struct arena_destructor_node *)NULL == it ) {
00276     return PR_FAILURE;
00277   }
00278 
00279   it->prev = arena->last_destructor;
00280   arena->last_destructor->next = it;
00281   arena->last_destructor = it;
00282   it->destructor = destructor;
00283   it->arg = arg;
00284 
00285   if( (nssArenaMark *)NULL != arena->last_mark ) {
00286     arena->last_mark->prev_destructor = it->prev;
00287     arena->last_mark->next_destructor = it->next;
00288   }
00289 
00290   return PR_SUCCESS;
00291 }
00292 
00293 NSS_IMPLEMENT PRStatus
00294 nssArena_deregisterDestructor
00295 (
00296   NSSArena *arena,
00297   void (*destructor)(void *argument),
00298   void *arg
00299 )
00300 {
00301   struct arena_destructor_node *it;
00302 
00303 #ifdef NSSDEBUG
00304   if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
00305     return PR_FAILURE;
00306   }
00307 #endif /* NSSDEBUG */
00308 
00309   for( it = arena->first_destructor; it; it = it->next ) {
00310     if( (it->destructor == destructor) && (it->arg == arg) ) {
00311       break;
00312     }
00313   }
00314 
00315   if( (struct arena_destructor_node *)NULL == it ) {
00316     nss_SetError(NSS_ERROR_NOT_FOUND);
00317     return PR_FAILURE;
00318   }
00319 
00320   if( it == arena->first_destructor ) {
00321     arena->first_destructor = it->next;
00322   }
00323 
00324   if( it == arena->last_destructor ) {
00325     arena->last_destructor = it->prev;
00326   }
00327 
00328   if( (struct arena_destructor_node *)NULL != it->prev ) {
00329     it->prev->next = it->next;
00330   }
00331 
00332   if( (struct arena_destructor_node *)NULL != it->next ) {
00333     it->next->prev = it->prev;
00334   }
00335 
00336   {
00337     nssArenaMark *m;
00338     for( m = arena->first_mark; m; m = m->next ) {
00339       if( m->next_destructor == it ) {
00340         m->next_destructor = it->next;
00341       }
00342       if( m->prev_destructor == it ) {
00343         m->prev_destructor = it->prev;
00344       }
00345     }
00346   }
00347 
00348   nss_ZFreeIf(it);
00349   return PR_SUCCESS;
00350 }
00351 
00352 static void
00353 nss_arena_call_destructor_chain
00354 (
00355   struct arena_destructor_node *it
00356 )
00357 {
00358   for( ; it ; it = it->next ) {
00359     (*(it->destructor))(it->arg);
00360   }
00361 }
00362 
00363 #endif /* ARENA_DESTRUCTOR_LIST */
00364 
00365 /*
00366  * NSSArena_Create
00367  *
00368  * This routine creates a new memory arena.  This routine may return
00369  * NULL upon error, in which case it will have created an error stack.
00370  *
00371  * The top-level error may be one of the following values:
00372  *  NSS_ERROR_NO_MEMORY
00373  *
00374  * Return value:
00375  *  NULL upon error
00376  *  A pointer to an NSSArena upon success
00377  */
00378 
00379 NSS_IMPLEMENT NSSArena *
00380 NSSArena_Create
00381 (
00382   void
00383 )
00384 {
00385   nss_ClearErrorStack();
00386   return nssArena_Create();
00387 }
00388 
00389 /*
00390  * nssArena_Create
00391  *
00392  * This routine creates a new memory arena.  This routine may return
00393  * NULL upon error, in which case it will have set an error on the 
00394  * error stack.
00395  *
00396  * The error may be one of the following values:
00397  *  NSS_ERROR_NO_MEMORY
00398  *
00399  * Return value:
00400  *  NULL upon error
00401  *  A pointer to an NSSArena upon success
00402  */
00403 
00404 NSS_IMPLEMENT NSSArena *
00405 nssArena_Create
00406 (
00407   void
00408 )
00409 {
00410   NSSArena *rv = (NSSArena *)NULL;
00411 
00412   rv = nss_ZNEW((NSSArena *)NULL, NSSArena);
00413   if( (NSSArena *)NULL == rv ) {
00414     nss_SetError(NSS_ERROR_NO_MEMORY);
00415     return (NSSArena *)NULL;
00416   }
00417 
00418   rv->lock = PR_NewLock();
00419   if( (PRLock *)NULL == rv->lock ) {
00420     (void)nss_ZFreeIf(rv);
00421     nss_SetError(NSS_ERROR_NO_MEMORY);
00422     return (NSSArena *)NULL;
00423   }
00424 
00425   /*
00426    * Arena sizes.  The current security code has 229 occurrences of
00427    * PORT_NewArena.  The default chunksizes specified break down as
00428    *
00429    *  Size    Mult.   Specified as
00430    *   512       1    512
00431    *  1024       7    1024
00432    *  2048       5    2048
00433    *  2048       5    CRMF_DEFAULT_ARENA_SIZE
00434    *  2048     190    DER_DEFAULT_CHUNKSIZE
00435    *  2048      20    SEC_ASN1_DEFAULT_ARENA_SIZE
00436    *  4096       1    4096
00437    *
00438    * Obviously this "default chunksize" flexibility isn't very 
00439    * useful to us, so I'll just pick 2048.
00440    */
00441 
00442   PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double));
00443 
00444 #ifdef DEBUG
00445   {
00446     PRStatus st;
00447     st = arena_add_pointer(rv);
00448     if( PR_SUCCESS != st ) {
00449       PL_FinishArenaPool(&rv->pool);
00450       PR_DestroyLock(rv->lock);
00451       (void)nss_ZFreeIf(rv);
00452       return (NSSArena *)NULL;
00453     }
00454   }
00455 #endif /* DEBUG */
00456 
00457   return rv;
00458 }
00459 
00460 /*
00461  * NSSArena_Destroy
00462  *
00463  * This routine will destroy the specified arena, freeing all memory
00464  * allocated from it.  This routine returns a PRStatus value; if 
00465  * successful, it will return PR_SUCCESS.  If unsuccessful, it will
00466  * create an error stack and return PR_FAILURE.
00467  *
00468  * The top-level error may be one of the following values:
00469  *  NSS_ERROR_INVALID_ARENA
00470  *
00471  * Return value:
00472  *  PR_SUCCESS upon success
00473  *  PR_FAILURE upon failure
00474  */
00475 
00476 NSS_IMPLEMENT PRStatus
00477 NSSArena_Destroy
00478 (
00479   NSSArena *arena
00480 )
00481 {
00482   nss_ClearErrorStack();
00483 
00484 #ifdef DEBUG
00485   if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
00486     return PR_FAILURE;
00487   }
00488 #endif /* DEBUG */
00489 
00490   return nssArena_Destroy(arena);
00491 }
00492 
00493 /*
00494  * nssArena_Destroy
00495  *
00496  * This routine will destroy the specified arena, freeing all memory
00497  * allocated from it.  This routine returns a PRStatus value; if 
00498  * successful, it will return PR_SUCCESS.  If unsuccessful, it will
00499  * set an error on the error stack and return PR_FAILURE.
00500  *
00501  * The error may be one of the following values:
00502  *  NSS_ERROR_INVALID_ARENA
00503  *
00504  * Return value:
00505  *  PR_SUCCESS
00506  *  PR_FAILURE
00507  */
00508 
00509 NSS_IMPLEMENT PRStatus
00510 nssArena_Destroy
00511 (
00512   NSSArena *arena
00513 )
00514 {
00515   PRLock *lock;
00516 
00517 #ifdef NSSDEBUG
00518   if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
00519     return PR_FAILURE;
00520   }
00521 #endif /* NSSDEBUG */
00522 
00523   if( (PRLock *)NULL == arena->lock ) {
00524     /* Just got destroyed */
00525     nss_SetError(NSS_ERROR_INVALID_ARENA);
00526     return PR_FAILURE;
00527   }
00528   PR_Lock(arena->lock);
00529   
00530 #ifdef DEBUG
00531   if( PR_SUCCESS != arena_remove_pointer(arena) ) {
00532     return PR_FAILURE;
00533   }
00534 #endif /* DEBUG */
00535 
00536 #ifdef ARENA_DESTRUCTOR_LIST
00537   /* Note that the arena is locked at this time */
00538   nss_arena_call_destructor_chain(arena->first_destructor);
00539 #endif /* ARENA_DESTRUCTOR_LIST */
00540 
00541   PL_FinishArenaPool(&arena->pool);
00542   lock = arena->lock;
00543   arena->lock = (PRLock *)NULL;
00544   PR_Unlock(lock);
00545   PR_DestroyLock(lock);
00546   (void)nss_ZFreeIf(arena);
00547   return PR_SUCCESS;
00548 }
00549 
00550 static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size);
00551 
00552 /*
00553  * nssArena_Mark
00554  *
00555  * This routine "marks" the current state of an arena.  Space
00556  * allocated after the arena has been marked can be freed by
00557  * releasing the arena back to the mark with nssArena_Release,
00558  * or committed by calling nssArena_Unmark.  When successful, 
00559  * this routine returns a valid nssArenaMark pointer.  This 
00560  * routine may return NULL upon error, in which case it will 
00561  * have set an error on the error stack.
00562  *
00563  * The error may be one of the following values:
00564  *  NSS_ERROR_INVALID_ARENA
00565  *  NSS_ERROR_NO_MEMORY
00566  *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
00567  *
00568  * Return value:
00569  *  NULL upon failure
00570  *  An nssArenaMark pointer upon success
00571  */
00572 
00573 NSS_IMPLEMENT nssArenaMark *
00574 nssArena_Mark
00575 (
00576   NSSArena *arena
00577 )
00578 {
00579   nssArenaMark *rv;
00580   void *p;
00581 
00582 #ifdef NSSDEBUG
00583   if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
00584     return (nssArenaMark *)NULL;
00585   }
00586 #endif /* NSSDEBUG */
00587 
00588   if( (PRLock *)NULL == arena->lock ) {
00589     /* Just got destroyed */
00590     nss_SetError(NSS_ERROR_INVALID_ARENA);
00591     return (nssArenaMark *)NULL;
00592   }
00593   PR_Lock(arena->lock);
00594 
00595 #ifdef ARENA_THREADMARK
00596   if( (PRThread *)NULL == arena->marking_thread ) {
00597     /* Unmarked.  Store our thread ID */
00598     arena->marking_thread = PR_GetCurrentThread();
00599     /* This call never fails. */
00600   } else {
00601     /* Marked.  Verify it's the current thread */
00602     if( PR_GetCurrentThread() != arena->marking_thread ) {
00603       PR_Unlock(arena->lock);
00604       nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
00605       return (nssArenaMark *)NULL;
00606     }
00607   }
00608 #endif /* ARENA_THREADMARK */
00609 
00610   p = PL_ARENA_MARK(&arena->pool);
00611   /* No error possible */
00612 
00613   /* Do this after the mark */
00614   rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark));
00615   if( (nssArenaMark *)NULL == rv ) {
00616     PR_Unlock(arena->lock);
00617     nss_SetError(NSS_ERROR_NO_MEMORY);
00618     return (nssArenaMark *)NULL;
00619   }
00620 
00621 #ifdef ARENA_THREADMARK
00622   if ( (nssArenaMark *)NULL == arena->first_mark) {
00623     arena->first_mark = rv;
00624     arena->last_mark = rv;
00625   } else {
00626     arena->last_mark->next = rv;
00627     arena->last_mark = rv;
00628   }
00629 #endif /* ARENA_THREADMARK */
00630 
00631   rv->mark = p;
00632   rv->magic = MARK_MAGIC;
00633 
00634 #ifdef ARENA_DESTRUCTOR_LIST
00635   rv->prev_destructor = arena->last_destructor;
00636 #endif /* ARENA_DESTRUCTOR_LIST */
00637 
00638   PR_Unlock(arena->lock);
00639 
00640   return rv;
00641 }
00642 
00643 /*
00644  * nss_arena_unmark_release
00645  *
00646  * This static routine implements the routines nssArena_Release
00647  * ans nssArena_Unmark, which are almost identical.
00648  */
00649 
00650 static PRStatus
00651 nss_arena_unmark_release
00652 (
00653   NSSArena *arena,
00654   nssArenaMark *arenaMark,
00655   PRBool release
00656 )
00657 {
00658   void *inner_mark;
00659 
00660 #ifdef NSSDEBUG
00661   if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
00662     return PR_FAILURE;
00663   }
00664 #endif /* NSSDEBUG */
00665 
00666   if( MARK_MAGIC != arenaMark->magic ) {
00667     nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
00668     return PR_FAILURE;
00669   }
00670 
00671   if( (PRLock *)NULL == arena->lock ) {
00672     /* Just got destroyed */
00673     nss_SetError(NSS_ERROR_INVALID_ARENA);
00674     return PR_FAILURE;
00675   }
00676   PR_Lock(arena->lock);
00677 
00678 #ifdef ARENA_THREADMARK
00679   if( (PRThread *)NULL != arena->marking_thread ) {
00680     if( PR_GetCurrentThread() != arena->marking_thread ) {
00681       PR_Unlock(arena->lock);
00682       nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
00683       return PR_FAILURE;
00684     }
00685   }
00686 #endif /* ARENA_THREADMARK */
00687 
00688   if( MARK_MAGIC != arenaMark->magic ) {
00689     /* Just got released */
00690     PR_Unlock(arena->lock);
00691     nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
00692     return PR_FAILURE;
00693   }
00694 
00695   arenaMark->magic = 0;
00696   inner_mark = arenaMark->mark;
00697 
00698 #ifdef ARENA_THREADMARK
00699   {
00700     nssArenaMark **pMark = &arena->first_mark;
00701     nssArenaMark *rest;
00702     nssArenaMark *last = (nssArenaMark *)NULL;
00703 
00704     /* Find this mark */
00705     while( *pMark != arenaMark ) {
00706       last = *pMark;
00707       pMark = &(*pMark)->next;
00708     }
00709 
00710     /* Remember the pointer, then zero it */
00711     rest = (*pMark)->next;
00712     *pMark = (nssArenaMark *)NULL;
00713 
00714     arena->last_mark = last;
00715 
00716     /* Invalidate any later marks being implicitly released */
00717     for( ; (nssArenaMark *)NULL != rest; rest = rest->next ) {
00718       rest->magic = 0;
00719     }
00720 
00721     /* If we just got rid of the first mark, clear the thread ID */
00722     if( (nssArenaMark *)NULL == arena->first_mark ) {
00723       arena->marking_thread = (PRThread *)NULL;
00724     }
00725   }
00726 #endif /* ARENA_THREADMARK */
00727 
00728   if( release ) {
00729 #ifdef ARENA_DESTRUCTOR_LIST
00730     if( (struct arena_destructor_node *)NULL != arenaMark->prev_destructor ) {
00731       arenaMark->prev_destructor->next = (struct arena_destructor_node *)NULL;
00732     }
00733     arena->last_destructor = arenaMark->prev_destructor;
00734 
00735     /* Note that the arena is locked at this time */
00736     nss_arena_call_destructor_chain(arenaMark->next_destructor);
00737 #endif /* ARENA_DESTRUCTOR_LIST */
00738 
00739     PR_ARENA_RELEASE(&arena->pool, inner_mark);
00740     /* No error return */
00741   }
00742 
00743   PR_Unlock(arena->lock);
00744   return PR_SUCCESS;
00745 }
00746 
00747 /*
00748  * nssArena_Release
00749  *
00750  * This routine invalidates and releases all memory allocated from
00751  * the specified arena after the point at which the specified mark
00752  * was obtained.  This routine returns a PRStatus value; if successful,
00753  * it will return PR_SUCCESS.  If unsuccessful, it will set an error
00754  * on the error stack and return PR_FAILURE.
00755  *
00756  * The error may be one of the following values:
00757  *  NSS_ERROR_INVALID_ARENA
00758  *  NSS_ERROR_INVALID_ARENA_MARK
00759  *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
00760  *
00761  * Return value:
00762  *  PR_SUCCESS
00763  *  PR_FAILURE
00764  */
00765 
00766 NSS_IMPLEMENT PRStatus
00767 nssArena_Release
00768 (
00769   NSSArena *arena,
00770   nssArenaMark *arenaMark
00771 )
00772 {
00773   return nss_arena_unmark_release(arena, arenaMark, PR_TRUE);
00774 }
00775 
00776 /*
00777  * nssArena_Unmark
00778  *
00779  * This routine "commits" the indicated mark and any marks after
00780  * it, making them unreleasable.  Note that any earlier marks can
00781  * still be released, and such a release will invalidate these
00782  * later unmarked regions.  If an arena is to be safely shared by
00783  * more than one thread, all marks must be either released or
00784  * unmarked.  This routine returns a PRStatus value; if successful,
00785  * it will return PR_SUCCESS.  If unsuccessful, it will set an error
00786  * on the error stack and return PR_FAILURE.
00787  *
00788  * The error may be one of the following values:
00789  *  NSS_ERROR_INVALID_ARENA
00790  *  NSS_ERROR_INVALID_ARENA_MARK
00791  *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
00792  *
00793  * Return value:
00794  *  PR_SUCCESS
00795  *  PR_FAILURE
00796  */
00797 
00798 NSS_IMPLEMENT PRStatus
00799 nssArena_Unmark
00800 (
00801   NSSArena *arena,
00802   nssArenaMark *arenaMark
00803 )
00804 {
00805   return nss_arena_unmark_release(arena, arenaMark, PR_FALSE);
00806 }
00807 
00808 /*
00809  * We prefix this header to all allocated blocks.  It is a multiple
00810  * of the alignment size.  Note that this usage of a header may make
00811  * purify spew bogus warnings about "potentially leaked blocks" of
00812  * memory; if that gets too annoying we can add in a pointer to the
00813  * header in the header itself.  There's not a lot of safety here;
00814  * maybe we should add a magic value?
00815  */
00816 struct pointer_header {
00817   NSSArena *arena;
00818   PRUint32 size;
00819 };
00820 
00821 static void *
00822 nss_zalloc_arena_locked
00823 (
00824   NSSArena *arena,
00825   PRUint32 size
00826 )
00827 {
00828   void *p;
00829   void *rv;
00830   struct pointer_header *h;
00831   PRUint32 my_size = size + sizeof(struct pointer_header);
00832   PR_ARENA_ALLOCATE(p, &arena->pool, my_size);
00833   if( (void *)NULL == p ) {
00834     nss_SetError(NSS_ERROR_NO_MEMORY);
00835     return (void *)NULL;
00836   }
00837   /* 
00838    * Do this before we unlock.  This way if the user is using
00839    * an arena in one thread while destroying it in another, he'll
00840    * fault/FMR in his code, not ours.
00841    */
00842   h = (struct pointer_header *)p;
00843   h->arena = arena;
00844   h->size = size;
00845   rv = (void *)((char *)h + sizeof(struct pointer_header));
00846   (void)nsslibc_memset(rv, 0, size);
00847   return rv;
00848 }
00849 
00850 /*
00851  * nss_ZAlloc
00852  *
00853  * This routine allocates and zeroes a section of memory of the 
00854  * size, and returns to the caller a pointer to that memory.  If
00855  * the optional arena argument is non-null, the memory will be
00856  * obtained from that arena; otherwise, the memory will be obtained
00857  * from the heap.  This routine may return NULL upon error, in
00858  * which case it will have set an error upon the error stack.  The
00859  * value specified for size may be zero; in which case a valid 
00860  * zero-length block of memory will be allocated.  This block may
00861  * be expanded by calling nss_ZRealloc.
00862  *
00863  * The error may be one of the following values:
00864  *  NSS_ERROR_INVALID_ARENA
00865  *  NSS_ERROR_NO_MEMORY
00866  *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
00867  *
00868  * Return value:
00869  *  NULL upon error
00870  *  A pointer to the new segment of zeroed memory
00871  */
00872 
00873 NSS_IMPLEMENT void *
00874 nss_ZAlloc
00875 (
00876   NSSArena *arenaOpt,
00877   PRUint32 size
00878 )
00879 {
00880   struct pointer_header *h;
00881   PRUint32 my_size = size + sizeof(struct pointer_header);
00882 
00883   if( my_size < sizeof(struct pointer_header) ) {
00884     /* Wrapped */
00885     nss_SetError(NSS_ERROR_NO_MEMORY);
00886     return (void *)NULL;
00887   }
00888 
00889   if( (NSSArena *)NULL == arenaOpt ) {
00890     /* Heap allocation, no locking required. */
00891     h = (struct pointer_header *)PR_Calloc(1, my_size);
00892     if( (struct pointer_header *)NULL == h ) {
00893       nss_SetError(NSS_ERROR_NO_MEMORY);
00894       return (void *)NULL;
00895     }
00896 
00897     h->arena = (NSSArena *)NULL;
00898     h->size = size;
00899     /* We used calloc: it's already zeroed */
00900 
00901     return (void *)((char *)h + sizeof(struct pointer_header));
00902   } else {
00903     void *rv;
00904     /* Arena allocation */
00905 #ifdef NSSDEBUG
00906     if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
00907       return (void *)NULL;
00908     }
00909 #endif /* NSSDEBUG */
00910 
00911     if( (PRLock *)NULL == arenaOpt->lock ) {
00912       /* Just got destroyed */
00913       nss_SetError(NSS_ERROR_INVALID_ARENA);
00914       return (void *)NULL;
00915     }
00916     PR_Lock(arenaOpt->lock);
00917 
00918 #ifdef ARENA_THREADMARK
00919     if( (PRThread *)NULL != arenaOpt->marking_thread ) {
00920       if( PR_GetCurrentThread() != arenaOpt->marking_thread ) {
00921         nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
00922         PR_Unlock(arenaOpt->lock);
00923         return (void *)NULL;
00924       }
00925     }
00926 #endif /* ARENA_THREADMARK */
00927 
00928     rv = nss_zalloc_arena_locked(arenaOpt, size);
00929 
00930     PR_Unlock(arenaOpt->lock);
00931     return rv;
00932   }
00933   /*NOTREACHED*/
00934 }
00935 
00936 /*
00937  * nss_ZFreeIf
00938  *
00939  * If the specified pointer is non-null, then the region of memory 
00940  * to which it points -- which must have been allocated with 
00941  * nss_ZAlloc -- will be zeroed and released.  This routine 
00942  * returns a PRStatus value; if successful, it will return PR_SUCCESS.
00943  * If unsuccessful, it will set an error on the error stack and return 
00944  * PR_FAILURE.
00945  *
00946  * The error may be one of the following values:
00947  *  NSS_ERROR_INVALID_POINTER
00948  *
00949  * Return value:
00950  *  PR_SUCCESS
00951  *  PR_FAILURE
00952  */
00953 
00954 NSS_IMPLEMENT PRStatus
00955 nss_ZFreeIf
00956 (
00957   void *pointer
00958 )
00959 {
00960   struct pointer_header *h;
00961 
00962   if( (void *)NULL == pointer ) {
00963     return PR_SUCCESS;
00964   }
00965 
00966   h = (struct pointer_header *)((char *)pointer
00967     - sizeof(struct pointer_header));
00968 
00969   /* Check any magic here */
00970 
00971   if( (NSSArena *)NULL == h->arena ) {
00972     /* Heap */
00973     (void)nsslibc_memset(pointer, 0, h->size);
00974     PR_Free(h);
00975     return PR_SUCCESS;
00976   } else {
00977     /* Arena */
00978 #ifdef NSSDEBUG
00979     if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) {
00980       return PR_FAILURE;
00981     }
00982 #endif /* NSSDEBUG */
00983 
00984     PR_Lock(h->arena->lock);
00985     if( (PRLock *)NULL == h->arena->lock ) {
00986       /* Just got destroyed.. so this pointer is invalid */
00987       nss_SetError(NSS_ERROR_INVALID_POINTER);
00988       return PR_FAILURE;
00989     }
00990 
00991     (void)nsslibc_memset(pointer, 0, h->size);
00992 
00993     /* No way to "free" it within an NSPR arena. */
00994 
00995     PR_Unlock(h->arena->lock);
00996     return PR_SUCCESS;
00997   }
00998   /*NOTREACHED*/
00999 }
01000 
01001 /*
01002  * nss_ZRealloc
01003  *
01004  * This routine reallocates a block of memory obtained by calling
01005  * nss_ZAlloc or nss_ZRealloc.  The portion of memory 
01006  * between the new and old sizes -- which is either being newly
01007  * obtained or released -- is in either case zeroed.  This routine 
01008  * may return NULL upon failure, in which case it will have placed 
01009  * an error on the error stack.
01010  *
01011  * The error may be one of the following values:
01012  *  NSS_ERROR_INVALID_POINTER
01013  *  NSS_ERROR_NO_MEMORY
01014  *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
01015  *
01016  * Return value:
01017  *  NULL upon error
01018  *  A pointer to the replacement segment of memory
01019  */
01020 
01021 NSS_EXTERN void *
01022 nss_ZRealloc
01023 (
01024   void *pointer,
01025   PRUint32 newSize
01026 )
01027 {
01028   struct pointer_header *h, *new_h;
01029   PRUint32 my_newSize = newSize + sizeof(struct pointer_header);
01030   void *rv;
01031 
01032   if( my_newSize < sizeof(struct pointer_header) ) {
01033     /* Wrapped */
01034     nss_SetError(NSS_ERROR_NO_MEMORY);
01035     return (void *)NULL;
01036   }
01037 
01038   if( (void *)NULL == pointer ) {
01039     nss_SetError(NSS_ERROR_INVALID_POINTER);
01040     return (void *)NULL;
01041   }
01042 
01043   h = (struct pointer_header *)((char *)pointer
01044     - sizeof(struct pointer_header));
01045 
01046   /* Check any magic here */
01047 
01048   if( newSize == h->size ) {
01049     /* saves thrashing */
01050     return pointer;
01051   }
01052 
01053   if( (NSSArena *)NULL == h->arena ) {
01054     /* Heap */
01055     new_h = (struct pointer_header *)PR_Calloc(1, my_newSize);
01056     if( (struct pointer_header *)NULL == new_h ) {
01057       nss_SetError(NSS_ERROR_NO_MEMORY);
01058       return (void *)NULL;
01059     }
01060 
01061     new_h->arena = (NSSArena *)NULL;
01062     new_h->size = newSize;
01063     rv = (void *)((char *)new_h + sizeof(struct pointer_header));
01064 
01065     if( newSize > h->size ) {
01066       (void)nsslibc_memcpy(rv, pointer, h->size);
01067       (void)nsslibc_memset(&((char *)rv)[ h->size ], 
01068                            0, (newSize - h->size));
01069     } else {
01070       (void)nsslibc_memcpy(rv, pointer, newSize);
01071     }
01072 
01073     (void)nsslibc_memset(pointer, 0, h->size);
01074     h->size = 0;
01075     PR_Free(h);
01076 
01077     return rv;
01078   } else {
01079     void *p;
01080     /* Arena */
01081 #ifdef NSSDEBUG
01082     if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) {
01083       return (void *)NULL;
01084     }
01085 #endif /* NSSDEBUG */
01086 
01087     PR_Lock(h->arena->lock);
01088     if( (PRLock *)NULL == h->arena->lock ) {
01089       /* Just got destroyed.. so this pointer is invalid */
01090       nss_SetError(NSS_ERROR_INVALID_POINTER);
01091       return (void *)NULL;
01092     }
01093 
01094 #ifdef ARENA_THREADMARK
01095     if( (PRThread *)NULL != h->arena->marking_thread ) {
01096       if( PR_GetCurrentThread() != h->arena->marking_thread ) {
01097         PR_Unlock(h->arena->lock);
01098         nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
01099         return (void *)NULL;
01100       }
01101     }
01102 #endif /* ARENA_THREADMARK */
01103 
01104     if( newSize < h->size ) {
01105       /*
01106        * We have no general way of returning memory to the arena
01107        * (mark/release doesn't work because things may have been
01108        * allocated after this object), so the memory is gone
01109        * anyway.  We might as well just return the same pointer to
01110        * the user, saying "yeah, uh-hunh, you can only use less of
01111        * it now."  We'll zero the leftover part, of course.  And
01112        * in fact we might as well *not* adjust h->size-- this way,
01113        * if the user reallocs back up to something not greater than
01114        * the original size, then voila, there's the memory!  This
01115        * way a thrash big/small/big/small doesn't burn up the arena.
01116        */
01117       char *extra = &((char *)pointer)[ newSize ];
01118       (void)nsslibc_memset(extra, 0, (h->size - newSize));
01119       PR_Unlock(h->arena->lock);
01120       return pointer;
01121     }
01122 
01123     PR_ARENA_ALLOCATE(p, &h->arena->pool, my_newSize);
01124     if( (void *)NULL == p ) {
01125       PR_Unlock(h->arena->lock);
01126       nss_SetError(NSS_ERROR_NO_MEMORY);
01127       return (void *)NULL;
01128     }
01129 
01130     new_h = (struct pointer_header *)p;
01131     new_h->arena = h->arena;
01132     new_h->size = newSize;
01133     rv = (void *)((char *)new_h + sizeof(struct pointer_header));
01134     if (rv != pointer) {
01135        (void)nsslibc_memcpy(rv, pointer, h->size);
01136        (void)nsslibc_memset(pointer, 0, h->size);
01137     }
01138     (void)nsslibc_memset(&((char *)rv)[ h->size ], 0, (newSize - h->size));
01139     h->arena = (NSSArena *)NULL;
01140     h->size = 0;
01141     PR_Unlock(new_h->arena->lock);
01142     return rv;
01143   }
01144   /*NOTREACHED*/
01145 }