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 */