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