nsprpub/pr/src/io/prfdcach.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/nsprpub/pr/src/io/prfdcach.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,292 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "primpl.h"
    1.10 +
    1.11 +#include <string.h>
    1.12 +
    1.13 +/*****************************************************************************/
    1.14 +/*****************************************************************************/
    1.15 +/************************** File descriptor caching **************************/
    1.16 +/*****************************************************************************/
    1.17 +/*****************************************************************************/
    1.18 +
    1.19 +/*
    1.20 +** This code is built into debuggable versions of NSPR to assist in
    1.21 +** finding misused file descriptors. Since file descritors (PRFileDesc)
    1.22 +** are identified by a pointer to their structure, they can be the
    1.23 +** target of dangling references. Furthermore, NSPR caches and tries
    1.24 +** to aggressively reuse file descriptors, leading to more ambiguity.
    1.25 +** The following code will allow a debugging client to set environment
    1.26 +** variables and control the number of file descriptors that will be
    1.27 +** preserved before they are recycled. The environment variables are
    1.28 +** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets
    1.29 +** the number of descriptors NSPR will allocate before beginning to
    1.30 +** recycle. The latter is the maximum number permitted in the cache
    1.31 +** (exclusive of those in use) at a time.
    1.32 +*/
    1.33 +typedef struct _PR_Fd_Cache
    1.34 +{
    1.35 +    PRLock *ml;
    1.36 +    PRIntn count;
    1.37 +    PRStack *stack;
    1.38 +    PRFileDesc *head, *tail;
    1.39 +    PRIntn limit_low, limit_high;
    1.40 +} _PR_Fd_Cache;
    1.41 +
    1.42 +static _PR_Fd_Cache _pr_fd_cache;
    1.43 +static PRFileDesc **stack2fd = &(((PRFileDesc*)NULL)->higher);
    1.44 +
    1.45 +
    1.46 +/*
    1.47 +** Get a FileDescriptor from the cache if one exists. If not allocate
    1.48 +** a new one from the heap.
    1.49 +*/
    1.50 +PRFileDesc *_PR_Getfd(void)
    1.51 +{
    1.52 +    PRFileDesc *fd;
    1.53 +    /*
    1.54 +    ** $$$
    1.55 +    ** This may look a little wasteful. We'll see. Right now I want to
    1.56 +    ** be able to toggle between caching and not at runtime to measure
    1.57 +    ** the differences. If it isn't too annoying, I'll leave it in.
    1.58 +    ** $$$$
    1.59 +    **
    1.60 +    ** The test is against _pr_fd_cache.limit_high. If that's zero,
    1.61 +    ** we're not doing the extended cache but going for performance.
    1.62 +    */
    1.63 +    if (0 == _pr_fd_cache.limit_high)
    1.64 +    {
    1.65 +        PRStackElem *pop;
    1.66 +        PR_ASSERT(NULL != _pr_fd_cache.stack);
    1.67 +        pop = PR_StackPop(_pr_fd_cache.stack);
    1.68 +        if (NULL == pop) goto allocate;
    1.69 +        fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
    1.70 +    }
    1.71 +    else
    1.72 +    {
    1.73 +        do
    1.74 +        {
    1.75 +            if (NULL == _pr_fd_cache.head) goto allocate;  /* nothing there */
    1.76 +            if (_pr_fd_cache.count < _pr_fd_cache.limit_low) goto allocate;
    1.77 +
    1.78 +            /* we "should" be able to extract an fd from the cache */
    1.79 +            PR_Lock(_pr_fd_cache.ml);  /* need the lock to do this safely */
    1.80 +            fd = _pr_fd_cache.head;  /* protected extraction */
    1.81 +            if (NULL == fd)  /* unexpected, but not fatal */
    1.82 +            {
    1.83 +                PR_ASSERT(0 == _pr_fd_cache.count);
    1.84 +                PR_ASSERT(NULL == _pr_fd_cache.tail);
    1.85 +            }
    1.86 +            else
    1.87 +            {
    1.88 +                _pr_fd_cache.count -= 1;
    1.89 +                _pr_fd_cache.head = fd->higher;
    1.90 +                if (NULL == _pr_fd_cache.head)
    1.91 +                {
    1.92 +                    PR_ASSERT(0 == _pr_fd_cache.count);
    1.93 +                    _pr_fd_cache.tail = NULL;
    1.94 +                }
    1.95 +                PR_ASSERT(&_pr_faulty_methods == fd->methods);
    1.96 +                PR_ASSERT(PR_INVALID_IO_LAYER == fd->identity);
    1.97 +                PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state);
    1.98 +            }
    1.99 +            PR_Unlock(_pr_fd_cache.ml);
   1.100 +
   1.101 +        } while (NULL == fd);  /* then go around and allocate a new one */
   1.102 +    }
   1.103 +
   1.104 +finished:
   1.105 +    fd->dtor = NULL;
   1.106 +    fd->lower = fd->higher = NULL;
   1.107 +    fd->identity = PR_NSPR_IO_LAYER;
   1.108 +    memset(fd->secret, 0, sizeof(PRFilePrivate));
   1.109 +    return fd;
   1.110 +
   1.111 +allocate:
   1.112 +    fd = PR_NEW(PRFileDesc);
   1.113 +    if (NULL != fd)
   1.114 +    {
   1.115 +        fd->secret = PR_NEW(PRFilePrivate);
   1.116 +        if (NULL == fd->secret) PR_DELETE(fd);
   1.117 +    }
   1.118 +    if (NULL != fd) goto finished;
   1.119 +    else return NULL;
   1.120 +
   1.121 +}  /* _PR_Getfd */
   1.122 +
   1.123 +/*
   1.124 +** Return a file descriptor to the cache unless there are too many in
   1.125 +** there already. If put in cache, clear the fields first.
   1.126 +*/
   1.127 +void _PR_Putfd(PRFileDesc *fd)
   1.128 +{
   1.129 +    PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity);
   1.130 +    fd->methods = &_pr_faulty_methods;
   1.131 +    fd->identity = PR_INVALID_IO_LAYER;
   1.132 +    fd->secret->state = _PR_FILEDESC_FREED;
   1.133 +
   1.134 +    if (0 == _pr_fd_cache.limit_high)
   1.135 +    {
   1.136 +        PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
   1.137 +    }
   1.138 +    else
   1.139 +    {
   1.140 +        if (_pr_fd_cache.count > _pr_fd_cache.limit_high)
   1.141 +        {
   1.142 +            PR_Free(fd->secret);
   1.143 +            PR_Free(fd);
   1.144 +        }
   1.145 +        else
   1.146 +        {
   1.147 +            PR_Lock(_pr_fd_cache.ml);
   1.148 +            if (NULL == _pr_fd_cache.tail)
   1.149 +            {
   1.150 +                PR_ASSERT(0 == _pr_fd_cache.count);
   1.151 +                PR_ASSERT(NULL == _pr_fd_cache.head);
   1.152 +                _pr_fd_cache.head = _pr_fd_cache.tail = fd;
   1.153 +            }
   1.154 +            else
   1.155 +            {
   1.156 +                PR_ASSERT(NULL == _pr_fd_cache.tail->higher);
   1.157 +                _pr_fd_cache.tail->higher = fd;
   1.158 +                _pr_fd_cache.tail = fd;  /* new value */
   1.159 +            }
   1.160 +            fd->higher = NULL;  /* always so */
   1.161 +            _pr_fd_cache.count += 1;  /* count the new entry */
   1.162 +            PR_Unlock(_pr_fd_cache.ml);
   1.163 +        }
   1.164 +    }
   1.165 +}  /* _PR_Putfd */
   1.166 +
   1.167 +PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high)
   1.168 +{
   1.169 +    /*
   1.170 +    ** This can be called at any time, may adjust the cache sizes,
   1.171 +    ** turn the caches off, or turn them on. It is not dependent
   1.172 +    ** on the compilation setting of DEBUG.
   1.173 +    */
   1.174 +    if (!_pr_initialized) _PR_ImplicitInitialization();
   1.175 +
   1.176 +    if (low > high) low = high;  /* sanity check the params */
   1.177 +    
   1.178 +    PR_Lock(_pr_fd_cache.ml);
   1.179 +    if (0 == high)  /* shutting down or staying down */
   1.180 +    {
   1.181 +        if (0 != _pr_fd_cache.limit_high)  /* shutting down */
   1.182 +        {
   1.183 +            _pr_fd_cache.limit_high = 0;  /* stop use */
   1.184 +            /*
   1.185 +            ** Hold the lock throughout - nobody's going to want it
   1.186 +            ** other than another caller to this routine. Just don't
   1.187 +            ** let that happen.
   1.188 +            **
   1.189 +            ** Put all the cached fds onto the new cache.
   1.190 +            */
   1.191 +            while (NULL != _pr_fd_cache.head)
   1.192 +            {
   1.193 +                PRFileDesc *fd = _pr_fd_cache.head;
   1.194 +                _pr_fd_cache.head = fd->higher;
   1.195 +                PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
   1.196 +            }
   1.197 +            _pr_fd_cache.limit_low = 0;
   1.198 +            _pr_fd_cache.tail = NULL;
   1.199 +            _pr_fd_cache.count = 0;
   1.200 +        }
   1.201 +    }
   1.202 +    else  /* starting up or just adjusting parameters */
   1.203 +    {
   1.204 +        PRBool was_using_stack = (0 == _pr_fd_cache.limit_high);
   1.205 +        _pr_fd_cache.limit_low = low;
   1.206 +        _pr_fd_cache.limit_high = high;
   1.207 +        if (was_using_stack)  /* was using stack - feed into cache */
   1.208 +        {
   1.209 +            PRStackElem *pop;
   1.210 +            while (NULL != (pop = PR_StackPop(_pr_fd_cache.stack)))
   1.211 +            {
   1.212 +                PRFileDesc *fd = (PRFileDesc*)
   1.213 +                    ((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
   1.214 +                if (NULL == _pr_fd_cache.tail) _pr_fd_cache.tail = fd;
   1.215 +                fd->higher = _pr_fd_cache.head;
   1.216 +                _pr_fd_cache.head = fd;
   1.217 +                _pr_fd_cache.count += 1;
   1.218 +            }
   1.219 +        }
   1.220 +    }
   1.221 +    PR_Unlock(_pr_fd_cache.ml);
   1.222 +    return PR_SUCCESS;
   1.223 +}  /* PR_SetFDCacheSize */
   1.224 +
   1.225 +void _PR_InitFdCache(void)
   1.226 +{
   1.227 +    /*
   1.228 +    ** The fd caching is enabled by default for DEBUG builds,
   1.229 +    ** disabled by default for OPT builds. That default can
   1.230 +    ** be overridden at runtime using environment variables
   1.231 +    ** or a super-wiz-bang API.
   1.232 +    */
   1.233 +    const char *low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW");
   1.234 +    const char *high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH");
   1.235 +
   1.236 +    /* 
   1.237 +    ** _low is allowed to be zero, _high is not.
   1.238 +    ** If _high is zero, we're not doing the caching.
   1.239 +    */
   1.240 +
   1.241 +    _pr_fd_cache.limit_low = 0;
   1.242 +#if defined(DEBUG)
   1.243 +    _pr_fd_cache.limit_high = FD_SETSIZE;
   1.244 +#else
   1.245 +    _pr_fd_cache.limit_high = 0;
   1.246 +#endif  /* defined(DEBUG) */
   1.247 +
   1.248 +    if (NULL != low) _pr_fd_cache.limit_low = atoi(low);
   1.249 +    if (NULL != high) _pr_fd_cache.limit_high = atoi(high);
   1.250 +
   1.251 +    if (_pr_fd_cache.limit_low < 0)
   1.252 +        _pr_fd_cache.limit_low = 0;
   1.253 +    if (_pr_fd_cache.limit_low > FD_SETSIZE)
   1.254 +        _pr_fd_cache.limit_low = FD_SETSIZE;
   1.255 +
   1.256 +    if (_pr_fd_cache.limit_high > FD_SETSIZE)
   1.257 +        _pr_fd_cache.limit_high = FD_SETSIZE;
   1.258 +
   1.259 +    if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low)
   1.260 +        _pr_fd_cache.limit_high = _pr_fd_cache.limit_low;
   1.261 +
   1.262 +    _pr_fd_cache.ml = PR_NewLock();
   1.263 +    PR_ASSERT(NULL != _pr_fd_cache.ml);
   1.264 +    _pr_fd_cache.stack = PR_CreateStack("FD");
   1.265 +    PR_ASSERT(NULL != _pr_fd_cache.stack);
   1.266 +
   1.267 +}  /* _PR_InitFdCache */
   1.268 +
   1.269 +void _PR_CleanupFdCache(void)
   1.270 +{
   1.271 +    PRFileDesc *fd, *next;
   1.272 +    PRStackElem *pop;
   1.273 +
   1.274 +    for (fd = _pr_fd_cache.head; fd != NULL; fd = next)
   1.275 +    {
   1.276 +        next = fd->higher;
   1.277 +        PR_DELETE(fd->secret);
   1.278 +        PR_DELETE(fd);
   1.279 +    }
   1.280 +    _pr_fd_cache.head = NULL;
   1.281 +    _pr_fd_cache.tail = NULL;
   1.282 +    _pr_fd_cache.count = 0;
   1.283 +    PR_DestroyLock(_pr_fd_cache.ml);
   1.284 +    _pr_fd_cache.ml = NULL;
   1.285 +    while ((pop = PR_StackPop(_pr_fd_cache.stack)) != NULL)
   1.286 +    {
   1.287 +        fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
   1.288 +        PR_DELETE(fd->secret);
   1.289 +        PR_DELETE(fd);
   1.290 +    }
   1.291 +    PR_DestroyStack(_pr_fd_cache.stack);
   1.292 +    _pr_fd_cache.stack = NULL;
   1.293 +}  /* _PR_CleanupFdCache */
   1.294 +
   1.295 +/* prfdcach.c */

mercurial