Back to index

lightning-sunbird  0.9+nobinonly
prfdcach.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 
00040 #include <string.h>
00041 
00042 /*****************************************************************************/
00043 /*****************************************************************************/
00044 /************************** File descriptor caching **************************/
00045 /*****************************************************************************/
00046 /*****************************************************************************/
00047 
00048 /*
00049 ** This code is built into debuggable versions of NSPR to assist in
00050 ** finding misused file descriptors. Since file descritors (PRFileDesc)
00051 ** are identified by a pointer to their structure, they can be the
00052 ** target of dangling references. Furthermore, NSPR caches and tries
00053 ** to aggressively reuse file descriptors, leading to more ambiguity.
00054 ** The following code will allow a debugging client to set environment
00055 ** variables and control the number of file descriptors that will be
00056 ** preserved before they are recycled. The environment variables are
00057 ** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets
00058 ** the number of descriptors NSPR will allocate before beginning to
00059 ** recycle. The latter is the maximum number permitted in the cache
00060 ** (exclusive of those in use) at a time.
00061 */
00062 typedef struct _PR_Fd_Cache
00063 {
00064     PRLock *ml;
00065     PRIntn count;
00066     PRStack *stack;
00067     PRFileDesc *head, *tail;
00068     PRIntn limit_low, limit_high;
00069 } _PR_Fd_Cache;
00070 
00071 static _PR_Fd_Cache _pr_fd_cache;
00072 static PRFileDesc **stack2fd = &(((PRFileDesc*)NULL)->higher);
00073 
00074 
00075 /*
00076 ** Get a FileDescriptor from the cache if one exists. If not allocate
00077 ** a new one from the heap.
00078 */
00079 PRFileDesc *_PR_Getfd(void)
00080 {
00081     PRFileDesc *fd;
00082     /*
00083     ** $$$
00084     ** This may look a little wasteful. We'll see. Right now I want to
00085     ** be able to toggle between caching and not at runtime to measure
00086     ** the differences. If it isn't too annoying, I'll leave it in.
00087     ** $$$$
00088     **
00089     ** The test is against _pr_fd_cache.limit_high. If that's zero,
00090     ** we're not doing the extended cache but going for performance.
00091     */
00092     if (0 == _pr_fd_cache.limit_high)
00093     {
00094         PRStackElem *pop;
00095         PR_ASSERT(NULL != _pr_fd_cache.stack);
00096         pop = PR_StackPop(_pr_fd_cache.stack);
00097         if (NULL == pop) goto allocate;
00098         fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
00099     }
00100     else
00101     {
00102         do
00103         {
00104             if (NULL == _pr_fd_cache.head) goto allocate;  /* nothing there */
00105             if (_pr_fd_cache.count < _pr_fd_cache.limit_low) goto allocate;
00106 
00107             /* we "should" be able to extract an fd from the cache */
00108             PR_Lock(_pr_fd_cache.ml);  /* need the lock to do this safely */
00109             fd = _pr_fd_cache.head;  /* protected extraction */
00110             if (NULL == fd)  /* unexpected, but not fatal */
00111             {
00112                 PR_ASSERT(0 == _pr_fd_cache.count);
00113                 PR_ASSERT(NULL == _pr_fd_cache.tail);
00114             }
00115             else
00116             {
00117                 _pr_fd_cache.count -= 1;
00118                 _pr_fd_cache.head = fd->higher;
00119                 if (NULL == _pr_fd_cache.head)
00120                 {
00121                     PR_ASSERT(0 == _pr_fd_cache.count);
00122                     _pr_fd_cache.tail = NULL;
00123                 }
00124                 PR_ASSERT(&_pr_faulty_methods == fd->methods);
00125                 PR_ASSERT(PR_INVALID_IO_LAYER == fd->identity);
00126                 PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state);
00127             }
00128             PR_Unlock(_pr_fd_cache.ml);
00129 
00130         } while (NULL == fd);  /* then go around and allocate a new one */
00131     }
00132 
00133 finished:
00134     fd->dtor = NULL;
00135     fd->lower = fd->higher = NULL;
00136     fd->identity = PR_NSPR_IO_LAYER;
00137     memset(fd->secret, 0, sizeof(PRFilePrivate));
00138     return fd;
00139 
00140 allocate:
00141     fd = PR_NEW(PRFileDesc);
00142     if (NULL != fd)
00143     {
00144         fd->secret = PR_NEW(PRFilePrivate);
00145         if (NULL == fd->secret) PR_DELETE(fd);
00146     }
00147     if (NULL != fd) goto finished;
00148     else return NULL;
00149 
00150 }  /* _PR_Getfd */
00151 
00152 /*
00153 ** Return a file descriptor to the cache unless there are too many in
00154 ** there already. If put in cache, clear the fields first.
00155 */
00156 void _PR_Putfd(PRFileDesc *fd)
00157 {
00158     PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity);
00159     fd->methods = &_pr_faulty_methods;
00160     fd->identity = PR_INVALID_IO_LAYER;
00161     fd->secret->state = _PR_FILEDESC_FREED;
00162 
00163     if (0 == _pr_fd_cache.limit_high)
00164     {
00165         PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
00166     }
00167     else
00168     {
00169         if (_pr_fd_cache.count > _pr_fd_cache.limit_high)
00170         {
00171             PR_Free(fd->secret);
00172             PR_Free(fd);
00173         }
00174         else
00175         {
00176             PR_Lock(_pr_fd_cache.ml);
00177             if (NULL == _pr_fd_cache.tail)
00178             {
00179                 PR_ASSERT(0 == _pr_fd_cache.count);
00180                 PR_ASSERT(NULL == _pr_fd_cache.head);
00181                 _pr_fd_cache.head = _pr_fd_cache.tail = fd;
00182             }
00183             else
00184             {
00185                 PR_ASSERT(NULL == _pr_fd_cache.tail->higher);
00186                 _pr_fd_cache.tail->higher = fd;
00187                 _pr_fd_cache.tail = fd;  /* new value */
00188             }
00189             fd->higher = NULL;  /* always so */
00190             _pr_fd_cache.count += 1;  /* count the new entry */
00191             PR_Unlock(_pr_fd_cache.ml);
00192         }
00193     }
00194 }  /* _PR_Putfd */
00195 
00196 PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high)
00197 {
00198     /*
00199     ** This can be called at any time, may adjust the cache sizes,
00200     ** turn the caches off, or turn them on. It is not dependent
00201     ** on the compilation setting of DEBUG.
00202     */
00203     if (!_pr_initialized) _PR_ImplicitInitialization();
00204 
00205     if (low > high) low = high;  /* sanity check the params */
00206     
00207     PR_Lock(_pr_fd_cache.ml);
00208     if (0 == high)  /* shutting down or staying down */
00209     {
00210         if (0 != _pr_fd_cache.limit_high)  /* shutting down */
00211         {
00212             _pr_fd_cache.limit_high = 0;  /* stop use */
00213             /*
00214             ** Hold the lock throughout - nobody's going to want it
00215             ** other than another caller to this routine. Just don't
00216             ** let that happen.
00217             **
00218             ** Put all the cached fds onto the new cache.
00219             */
00220             while (NULL != _pr_fd_cache.head)
00221             {
00222                 PRFileDesc *fd = _pr_fd_cache.head;
00223                 _pr_fd_cache.head = fd->higher;
00224                 PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
00225             }
00226             _pr_fd_cache.limit_low = 0;
00227             _pr_fd_cache.tail = NULL;
00228             _pr_fd_cache.count = 0;
00229         }
00230     }
00231     else  /* starting up or just adjusting parameters */
00232     {
00233         PRBool was_using_stack = (0 == _pr_fd_cache.limit_high);
00234         _pr_fd_cache.limit_low = low;
00235         _pr_fd_cache.limit_high = high;
00236         if (was_using_stack)  /* was using stack - feed into cache */
00237         {
00238             PRStackElem *pop;
00239             while (NULL != (pop = PR_StackPop(_pr_fd_cache.stack)))
00240             {
00241                 PRFileDesc *fd = (PRFileDesc*)
00242                     ((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
00243                 if (NULL == _pr_fd_cache.tail) _pr_fd_cache.tail = fd;
00244                 fd->higher = _pr_fd_cache.head;
00245                 _pr_fd_cache.head = fd;
00246                 _pr_fd_cache.count += 1;
00247             }
00248         }
00249     }
00250     PR_Unlock(_pr_fd_cache.ml);
00251     return PR_SUCCESS;
00252 }  /* PR_SetFDCacheSize */
00253 
00254 void _PR_InitFdCache(void)
00255 {
00256     /*
00257     ** The fd caching is enabled by default for DEBUG builds,
00258     ** disabled by default for OPT builds. That default can
00259     ** be overridden at runtime using environment variables
00260     ** or a super-wiz-bang API.
00261     */
00262     const char *low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW");
00263     const char *high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH");
00264 
00265     /* 
00266     ** _low is allowed to be zero, _high is not.
00267     ** If _high is zero, we're not doing the caching.
00268     */
00269 
00270     _pr_fd_cache.limit_low = 0;
00271 #if defined(DEBUG)
00272     _pr_fd_cache.limit_high = FD_SETSIZE;
00273 #else
00274     _pr_fd_cache.limit_high = 0;
00275 #endif  /* defined(DEBUG) */
00276 
00277     if (NULL != low) _pr_fd_cache.limit_low = atoi(low);
00278     if (NULL != high) _pr_fd_cache.limit_high = atoi(high);
00279 
00280     if (_pr_fd_cache.limit_low < 0)
00281         _pr_fd_cache.limit_low = 0;
00282     if (_pr_fd_cache.limit_low > FD_SETSIZE)
00283         _pr_fd_cache.limit_low = FD_SETSIZE;
00284 
00285     if (_pr_fd_cache.limit_high > FD_SETSIZE)
00286         _pr_fd_cache.limit_high = FD_SETSIZE;
00287 
00288     if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low)
00289         _pr_fd_cache.limit_high = _pr_fd_cache.limit_low;
00290 
00291     _pr_fd_cache.ml = PR_NewLock();
00292     PR_ASSERT(NULL != _pr_fd_cache.ml);
00293     _pr_fd_cache.stack = PR_CreateStack("FD");
00294     PR_ASSERT(NULL != _pr_fd_cache.stack);
00295 
00296 }  /* _PR_InitFdCache */
00297 
00298 void _PR_CleanupFdCache(void)
00299 {
00300     PRFileDesc *fd, *next;
00301     PRStackElem *pop;
00302 
00303     for (fd = _pr_fd_cache.head; fd != NULL; fd = next)
00304     {
00305         next = fd->higher;
00306         PR_DELETE(fd->secret);
00307         PR_DELETE(fd);
00308     }
00309     PR_DestroyLock(_pr_fd_cache.ml);
00310     while ((pop = PR_StackPop(_pr_fd_cache.stack)) != NULL)
00311     {
00312         fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
00313         PR_DELETE(fd->secret);
00314         PR_DELETE(fd);
00315     }
00316     PR_DestroyStack(_pr_fd_cache.stack);
00317 }  /* _PR_CleanupFdCache */
00318 
00319 /* prfdcach.c */