prfdcach.c (6950B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "primpl.h" 7 8 #include <string.h> 9 10 /*****************************************************************************/ 11 /*****************************************************************************/ 12 /************************** File descriptor caching **************************/ 13 /*****************************************************************************/ 14 /*****************************************************************************/ 15 16 /* 17 ** This code is built into debuggable versions of NSPR to assist in 18 ** finding misused file descriptors. Since file descritors (PRFileDesc) 19 ** are identified by a pointer to their structure, they can be the 20 ** target of dangling references. Furthermore, NSPR caches and tries 21 ** to aggressively reuse file descriptors, leading to more ambiguity. 22 ** The following code will allow a debugging client to set environment 23 ** variables and control the number of file descriptors that will be 24 ** preserved before they are recycled. The environment variables are 25 ** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets 26 ** the number of descriptors NSPR will allocate before beginning to 27 ** recycle. The latter is the maximum number permitted in the cache 28 ** (exclusive of those in use) at a time. 29 */ 30 typedef struct _PR_Fd_Cache { 31 PRLock* ml; 32 PRIntn count; 33 PRFileDesc *head, *tail; 34 PRIntn limit_low, limit_high; 35 } _PR_Fd_Cache; 36 37 static _PR_Fd_Cache _pr_fd_cache; 38 39 /* 40 ** Get a FileDescriptor from the cache if one exists. If not allocate 41 ** a new one from the heap. 42 */ 43 PRFileDesc* _PR_Getfd(void) { 44 PRFileDesc* fd; 45 /* 46 ** $$$ 47 ** This may look a little wasteful. We'll see. Right now I want to 48 ** be able to toggle between caching and not at runtime to measure 49 ** the differences. If it isn't too annoying, I'll leave it in. 50 ** $$$$ 51 ** 52 ** The test is against _pr_fd_cache.limit_high. If that's zero, 53 ** we're not doing the extended cache but going for performance. 54 */ 55 if (0 == _pr_fd_cache.limit_high) { 56 goto allocate; 57 } else { 58 do { 59 if (NULL == _pr_fd_cache.head) { 60 goto allocate; /* nothing there */ 61 } 62 if (_pr_fd_cache.count < _pr_fd_cache.limit_low) { 63 goto allocate; 64 } 65 66 /* we "should" be able to extract an fd from the cache */ 67 PR_Lock(_pr_fd_cache.ml); /* need the lock to do this safely */ 68 fd = _pr_fd_cache.head; /* protected extraction */ 69 if (NULL == fd) /* unexpected, but not fatal */ 70 { 71 PR_ASSERT(0 == _pr_fd_cache.count); 72 PR_ASSERT(NULL == _pr_fd_cache.tail); 73 } else { 74 _pr_fd_cache.count -= 1; 75 _pr_fd_cache.head = fd->higher; 76 if (NULL == _pr_fd_cache.head) { 77 PR_ASSERT(0 == _pr_fd_cache.count); 78 _pr_fd_cache.tail = NULL; 79 } 80 PR_ASSERT(&_pr_faulty_methods == fd->methods); 81 PR_ASSERT(PR_INVALID_IO_LAYER == fd->identity); 82 PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state); 83 } 84 PR_Unlock(_pr_fd_cache.ml); 85 86 } while (NULL == fd); /* then go around and allocate a new one */ 87 } 88 89 finished: 90 fd->dtor = NULL; 91 fd->lower = fd->higher = NULL; 92 fd->identity = PR_NSPR_IO_LAYER; 93 memset(fd->secret, 0, sizeof(PRFilePrivate)); 94 return fd; 95 96 allocate: 97 fd = PR_NEW(PRFileDesc); 98 if (NULL != fd) { 99 fd->secret = PR_NEW(PRFilePrivate); 100 if (NULL == fd->secret) { 101 PR_DELETE(fd); 102 } 103 } 104 if (NULL != fd) { 105 goto finished; 106 } else { 107 return NULL; 108 } 109 110 } /* _PR_Getfd */ 111 112 /* 113 ** Return a file descriptor to the cache unless there are too many in 114 ** there already. If put in cache, clear the fields first. 115 */ 116 void _PR_Putfd(PRFileDesc* fd) { 117 PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity); 118 fd->methods = &_pr_faulty_methods; 119 fd->identity = PR_INVALID_IO_LAYER; 120 fd->secret->state = _PR_FILEDESC_FREED; 121 122 if (0 != _pr_fd_cache.limit_high) { 123 if (_pr_fd_cache.count < _pr_fd_cache.limit_high) { 124 PR_Lock(_pr_fd_cache.ml); 125 if (NULL == _pr_fd_cache.tail) { 126 PR_ASSERT(0 == _pr_fd_cache.count); 127 PR_ASSERT(NULL == _pr_fd_cache.head); 128 _pr_fd_cache.head = _pr_fd_cache.tail = fd; 129 } else { 130 PR_ASSERT(NULL == _pr_fd_cache.tail->higher); 131 _pr_fd_cache.tail->higher = fd; 132 _pr_fd_cache.tail = fd; /* new value */ 133 } 134 fd->higher = NULL; /* always so */ 135 _pr_fd_cache.count += 1; /* count the new entry */ 136 PR_Unlock(_pr_fd_cache.ml); 137 return; 138 } 139 } 140 141 PR_Free(fd->secret); 142 PR_Free(fd); 143 } /* _PR_Putfd */ 144 145 PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high) { 146 /* 147 ** This can be called at any time, may adjust the cache sizes, 148 ** turn the caches off, or turn them on. It is not dependent 149 ** on the compilation setting of DEBUG. 150 */ 151 if (!_pr_initialized) { 152 _PR_ImplicitInitialization(); 153 } 154 155 if (low > high) { 156 low = high; /* sanity check the params */ 157 } 158 159 PR_Lock(_pr_fd_cache.ml); 160 _pr_fd_cache.limit_high = high; 161 _pr_fd_cache.limit_low = low; 162 PR_Unlock(_pr_fd_cache.ml); 163 return PR_SUCCESS; 164 } /* PR_SetFDCacheSize */ 165 166 void _PR_InitFdCache(void) { 167 /* 168 ** The fd caching is enabled by default for DEBUG builds, 169 ** disabled by default for OPT builds. That default can 170 ** be overridden at runtime using environment variables 171 ** or a super-wiz-bang API. 172 */ 173 const char* low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW"); 174 const char* high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH"); 175 176 /* 177 ** _low is allowed to be zero, _high is not. 178 ** If _high is zero, we're not doing the caching. 179 */ 180 181 _pr_fd_cache.limit_low = 0; 182 #if defined(DEBUG) 183 _pr_fd_cache.limit_high = FD_SETSIZE; 184 #else 185 _pr_fd_cache.limit_high = 0; 186 #endif /* defined(DEBUG) */ 187 188 if (NULL != low) { 189 _pr_fd_cache.limit_low = atoi(low); 190 } 191 if (NULL != high) { 192 _pr_fd_cache.limit_high = atoi(high); 193 } 194 195 if (_pr_fd_cache.limit_low < 0) { 196 _pr_fd_cache.limit_low = 0; 197 } 198 if (_pr_fd_cache.limit_low > FD_SETSIZE) { 199 _pr_fd_cache.limit_low = FD_SETSIZE; 200 } 201 202 if (_pr_fd_cache.limit_high > FD_SETSIZE) { 203 _pr_fd_cache.limit_high = FD_SETSIZE; 204 } 205 206 if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low) { 207 _pr_fd_cache.limit_high = _pr_fd_cache.limit_low; 208 } 209 210 _pr_fd_cache.ml = PR_NewLock(); 211 PR_ASSERT(NULL != _pr_fd_cache.ml); 212 213 } /* _PR_InitFdCache */ 214 215 void _PR_CleanupFdCache(void) { 216 PRFileDesc *fd, *next; 217 218 for (fd = _pr_fd_cache.head; fd != NULL; fd = next) { 219 next = fd->higher; 220 PR_DELETE(fd->secret); 221 PR_DELETE(fd); 222 } 223 _pr_fd_cache.head = NULL; 224 _pr_fd_cache.tail = NULL; 225 _pr_fd_cache.count = 0; 226 PR_DestroyLock(_pr_fd_cache.ml); 227 _pr_fd_cache.ml = NULL; 228 } /* _PR_CleanupFdCache */ 229 230 /* prfdcach.c */