LCOV - code coverage report
Current view: directory - nsprpub/pr/src/io - prfdcach.c (source / functions) Found Hit Coverage
Test: app.info Lines: 120 76 63.3 %
Date: 2012-06-02 Functions: 5 4 80.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Netscape Portable Runtime (NSPR).
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998-2000
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "primpl.h"
      39                 : 
      40                 : #include <string.h>
      41                 : 
      42                 : /*****************************************************************************/
      43                 : /*****************************************************************************/
      44                 : /************************** File descriptor caching **************************/
      45                 : /*****************************************************************************/
      46                 : /*****************************************************************************/
      47                 : 
      48                 : /*
      49                 : ** This code is built into debuggable versions of NSPR to assist in
      50                 : ** finding misused file descriptors. Since file descritors (PRFileDesc)
      51                 : ** are identified by a pointer to their structure, they can be the
      52                 : ** target of dangling references. Furthermore, NSPR caches and tries
      53                 : ** to aggressively reuse file descriptors, leading to more ambiguity.
      54                 : ** The following code will allow a debugging client to set environment
      55                 : ** variables and control the number of file descriptors that will be
      56                 : ** preserved before they are recycled. The environment variables are
      57                 : ** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets
      58                 : ** the number of descriptors NSPR will allocate before beginning to
      59                 : ** recycle. The latter is the maximum number permitted in the cache
      60                 : ** (exclusive of those in use) at a time.
      61                 : */
      62                 : typedef struct _PR_Fd_Cache
      63                 : {
      64                 :     PRLock *ml;
      65                 :     PRIntn count;
      66                 :     PRStack *stack;
      67                 :     PRFileDesc *head, *tail;
      68                 :     PRIntn limit_low, limit_high;
      69                 : } _PR_Fd_Cache;
      70                 : 
      71                 : static _PR_Fd_Cache _pr_fd_cache;
      72                 : static PRFileDesc **stack2fd = &(((PRFileDesc*)NULL)->higher);
      73                 : 
      74                 : 
      75                 : /*
      76                 : ** Get a FileDescriptor from the cache if one exists. If not allocate
      77                 : ** a new one from the heap.
      78                 : */
      79          429894 : PRFileDesc *_PR_Getfd(void)
      80                 : {
      81                 :     PRFileDesc *fd;
      82                 :     /*
      83                 :     ** $$$
      84                 :     ** This may look a little wasteful. We'll see. Right now I want to
      85                 :     ** be able to toggle between caching and not at runtime to measure
      86                 :     ** the differences. If it isn't too annoying, I'll leave it in.
      87                 :     ** $$$$
      88                 :     **
      89                 :     ** The test is against _pr_fd_cache.limit_high. If that's zero,
      90                 :     ** we're not doing the extended cache but going for performance.
      91                 :     */
      92          429894 :     if (0 == _pr_fd_cache.limit_high)
      93                 :     {
      94                 :         PRStackElem *pop;
      95               0 :         PR_ASSERT(NULL != _pr_fd_cache.stack);
      96               0 :         pop = PR_StackPop(_pr_fd_cache.stack);
      97               0 :         if (NULL == pop) goto allocate;
      98               0 :         fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
      99                 :     }
     100                 :     else
     101                 :     {
     102                 :         do
     103                 :         {
     104          429894 :             if (NULL == _pr_fd_cache.head) goto allocate;  /* nothing there */
     105          361406 :             if (_pr_fd_cache.count < _pr_fd_cache.limit_low) goto allocate;
     106                 : 
     107                 :             /* we "should" be able to extract an fd from the cache */
     108          361406 :             PR_Lock(_pr_fd_cache.ml);  /* need the lock to do this safely */
     109          361406 :             fd = _pr_fd_cache.head;  /* protected extraction */
     110          361406 :             if (NULL == fd)  /* unexpected, but not fatal */
     111                 :             {
     112               0 :                 PR_ASSERT(0 == _pr_fd_cache.count);
     113               0 :                 PR_ASSERT(NULL == _pr_fd_cache.tail);
     114                 :             }
     115                 :             else
     116                 :             {
     117          361406 :                 _pr_fd_cache.count -= 1;
     118          361406 :                 _pr_fd_cache.head = fd->higher;
     119          361406 :                 if (NULL == _pr_fd_cache.head)
     120                 :                 {
     121          289413 :                     PR_ASSERT(0 == _pr_fd_cache.count);
     122          289413 :                     _pr_fd_cache.tail = NULL;
     123                 :                 }
     124          361406 :                 PR_ASSERT(&_pr_faulty_methods == fd->methods);
     125          361406 :                 PR_ASSERT(PR_INVALID_IO_LAYER == fd->identity);
     126          361406 :                 PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state);
     127                 :             }
     128          361406 :             PR_Unlock(_pr_fd_cache.ml);
     129                 : 
     130          361406 :         } while (NULL == fd);  /* then go around and allocate a new one */
     131                 :     }
     132                 : 
     133                 : finished:
     134          429894 :     fd->dtor = NULL;
     135          429894 :     fd->lower = fd->higher = NULL;
     136          429894 :     fd->identity = PR_NSPR_IO_LAYER;
     137          429894 :     memset(fd->secret, 0, sizeof(PRFilePrivate));
     138          429894 :     return fd;
     139                 : 
     140                 : allocate:
     141           68488 :     fd = PR_NEW(PRFileDesc);
     142           68488 :     if (NULL != fd)
     143                 :     {
     144           68488 :         fd->secret = PR_NEW(PRFilePrivate);
     145           68488 :         if (NULL == fd->secret) PR_DELETE(fd);
     146                 :     }
     147           68488 :     if (NULL != fd) goto finished;
     148               0 :     else return NULL;
     149                 : 
     150                 : }  /* _PR_Getfd */
     151                 : 
     152                 : /*
     153                 : ** Return a file descriptor to the cache unless there are too many in
     154                 : ** there already. If put in cache, clear the fields first.
     155                 : */
     156          370091 : void _PR_Putfd(PRFileDesc *fd)
     157                 : {
     158          370091 :     PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity);
     159          370091 :     fd->methods = &_pr_faulty_methods;
     160          370091 :     fd->identity = PR_INVALID_IO_LAYER;
     161          370091 :     fd->secret->state = _PR_FILEDESC_FREED;
     162                 : 
     163          370091 :     if (0 == _pr_fd_cache.limit_high)
     164                 :     {
     165               0 :         PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
     166                 :     }
     167                 :     else
     168                 :     {
     169          370091 :         if (_pr_fd_cache.count > _pr_fd_cache.limit_high)
     170                 :         {
     171               0 :             PR_Free(fd->secret);
     172               0 :             PR_Free(fd);
     173                 :         }
     174                 :         else
     175                 :         {
     176          370091 :             PR_Lock(_pr_fd_cache.ml);
     177          370091 :             if (NULL == _pr_fd_cache.tail)
     178                 :             {
     179          290994 :                 PR_ASSERT(0 == _pr_fd_cache.count);
     180          290994 :                 PR_ASSERT(NULL == _pr_fd_cache.head);
     181          290994 :                 _pr_fd_cache.head = _pr_fd_cache.tail = fd;
     182                 :             }
     183                 :             else
     184                 :             {
     185           79097 :                 PR_ASSERT(NULL == _pr_fd_cache.tail->higher);
     186           79097 :                 _pr_fd_cache.tail->higher = fd;
     187           79097 :                 _pr_fd_cache.tail = fd;  /* new value */
     188                 :             }
     189          370091 :             fd->higher = NULL;  /* always so */
     190          370091 :             _pr_fd_cache.count += 1;  /* count the new entry */
     191          370091 :             PR_Unlock(_pr_fd_cache.ml);
     192                 :         }
     193                 :     }
     194          370091 : }  /* _PR_Putfd */
     195                 : 
     196               0 : PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high)
     197                 : {
     198                 :     /*
     199                 :     ** This can be called at any time, may adjust the cache sizes,
     200                 :     ** turn the caches off, or turn them on. It is not dependent
     201                 :     ** on the compilation setting of DEBUG.
     202                 :     */
     203               0 :     if (!_pr_initialized) _PR_ImplicitInitialization();
     204                 : 
     205               0 :     if (low > high) low = high;  /* sanity check the params */
     206                 :     
     207               0 :     PR_Lock(_pr_fd_cache.ml);
     208               0 :     if (0 == high)  /* shutting down or staying down */
     209                 :     {
     210               0 :         if (0 != _pr_fd_cache.limit_high)  /* shutting down */
     211                 :         {
     212               0 :             _pr_fd_cache.limit_high = 0;  /* stop use */
     213                 :             /*
     214                 :             ** Hold the lock throughout - nobody's going to want it
     215                 :             ** other than another caller to this routine. Just don't
     216                 :             ** let that happen.
     217                 :             **
     218                 :             ** Put all the cached fds onto the new cache.
     219                 :             */
     220               0 :             while (NULL != _pr_fd_cache.head)
     221                 :             {
     222               0 :                 PRFileDesc *fd = _pr_fd_cache.head;
     223               0 :                 _pr_fd_cache.head = fd->higher;
     224               0 :                 PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
     225                 :             }
     226               0 :             _pr_fd_cache.limit_low = 0;
     227               0 :             _pr_fd_cache.tail = NULL;
     228               0 :             _pr_fd_cache.count = 0;
     229                 :         }
     230                 :     }
     231                 :     else  /* starting up or just adjusting parameters */
     232                 :     {
     233               0 :         PRBool was_using_stack = (0 == _pr_fd_cache.limit_high);
     234               0 :         _pr_fd_cache.limit_low = low;
     235               0 :         _pr_fd_cache.limit_high = high;
     236               0 :         if (was_using_stack)  /* was using stack - feed into cache */
     237                 :         {
     238                 :             PRStackElem *pop;
     239               0 :             while (NULL != (pop = PR_StackPop(_pr_fd_cache.stack)))
     240                 :             {
     241               0 :                 PRFileDesc *fd = (PRFileDesc*)
     242               0 :                     ((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
     243               0 :                 if (NULL == _pr_fd_cache.tail) _pr_fd_cache.tail = fd;
     244               0 :                 fd->higher = _pr_fd_cache.head;
     245               0 :                 _pr_fd_cache.head = fd;
     246               0 :                 _pr_fd_cache.count += 1;
     247                 :             }
     248                 :         }
     249                 :     }
     250               0 :     PR_Unlock(_pr_fd_cache.ml);
     251               0 :     return PR_SUCCESS;
     252                 : }  /* PR_SetFDCacheSize */
     253                 : 
     254           20034 : void _PR_InitFdCache(void)
     255                 : {
     256                 :     /*
     257                 :     ** The fd caching is enabled by default for DEBUG builds,
     258                 :     ** disabled by default for OPT builds. That default can
     259                 :     ** be overridden at runtime using environment variables
     260                 :     ** or a super-wiz-bang API.
     261                 :     */
     262           20034 :     const char *low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW");
     263           20034 :     const char *high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH");
     264                 : 
     265                 :     /* 
     266                 :     ** _low is allowed to be zero, _high is not.
     267                 :     ** If _high is zero, we're not doing the caching.
     268                 :     */
     269                 : 
     270           20034 :     _pr_fd_cache.limit_low = 0;
     271                 : #if defined(DEBUG)
     272           20034 :     _pr_fd_cache.limit_high = FD_SETSIZE;
     273                 : #else
     274                 :     _pr_fd_cache.limit_high = 0;
     275                 : #endif  /* defined(DEBUG) */
     276                 : 
     277           20034 :     if (NULL != low) _pr_fd_cache.limit_low = atoi(low);
     278           20034 :     if (NULL != high) _pr_fd_cache.limit_high = atoi(high);
     279                 : 
     280           20034 :     if (_pr_fd_cache.limit_low < 0)
     281               0 :         _pr_fd_cache.limit_low = 0;
     282           20034 :     if (_pr_fd_cache.limit_low > FD_SETSIZE)
     283               0 :         _pr_fd_cache.limit_low = FD_SETSIZE;
     284                 : 
     285           20034 :     if (_pr_fd_cache.limit_high > FD_SETSIZE)
     286               0 :         _pr_fd_cache.limit_high = FD_SETSIZE;
     287                 : 
     288           20034 :     if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low)
     289               0 :         _pr_fd_cache.limit_high = _pr_fd_cache.limit_low;
     290                 : 
     291           20034 :     _pr_fd_cache.ml = PR_NewLock();
     292           20034 :     PR_ASSERT(NULL != _pr_fd_cache.ml);
     293           20034 :     _pr_fd_cache.stack = PR_CreateStack("FD");
     294           20034 :     PR_ASSERT(NULL != _pr_fd_cache.stack);
     295                 : 
     296           20034 : }  /* _PR_InitFdCache */
     297                 : 
     298             140 : void _PR_CleanupFdCache(void)
     299                 : {
     300                 :     PRFileDesc *fd, *next;
     301                 :     PRStackElem *pop;
     302                 : 
     303             620 :     for (fd = _pr_fd_cache.head; fd != NULL; fd = next)
     304                 :     {
     305             480 :         next = fd->higher;
     306             480 :         PR_DELETE(fd->secret);
     307             480 :         PR_DELETE(fd);
     308                 :     }
     309             140 :     _pr_fd_cache.head = NULL;
     310             140 :     _pr_fd_cache.tail = NULL;
     311             140 :     _pr_fd_cache.count = 0;
     312             140 :     PR_DestroyLock(_pr_fd_cache.ml);
     313             140 :     _pr_fd_cache.ml = NULL;
     314             280 :     while ((pop = PR_StackPop(_pr_fd_cache.stack)) != NULL)
     315                 :     {
     316               0 :         fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
     317               0 :         PR_DELETE(fd->secret);
     318               0 :         PR_DELETE(fd);
     319                 :     }
     320             140 :     PR_DestroyStack(_pr_fd_cache.stack);
     321             140 :     _pr_fd_cache.stack = NULL;
     322             140 : }  /* _PR_CleanupFdCache */
     323                 : 
     324                 : /* prfdcach.c */

Generated by: LCOV version 1.7