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/. */
10 /*****************************************************************************/
11 /*****************************************************************************/
12 /************************** File descriptor caching **************************/
13 /*****************************************************************************/
14 /*****************************************************************************/
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.
30 typedef struct _PR_Fd_Cache
{
33 PRFileDesc
*head
, *tail
;
34 PRIntn limit_low
, limit_high
;
37 static _PR_Fd_Cache _pr_fd_cache
;
40 ** Get a FileDescriptor from the cache if one exists. If not allocate
41 ** a new one from the heap.
43 PRFileDesc
* _PR_Getfd(void) {
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.
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.
55 if (0 == _pr_fd_cache
.limit_high
) {
59 if (NULL
== _pr_fd_cache
.head
) {
60 goto allocate
; /* nothing there */
62 if (_pr_fd_cache
.count
< _pr_fd_cache
.limit_low
) {
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 */
71 PR_ASSERT(0 == _pr_fd_cache
.count
);
72 PR_ASSERT(NULL
== _pr_fd_cache
.tail
);
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
;
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
);
84 PR_Unlock(_pr_fd_cache
.ml
);
86 } while (NULL
== fd
); /* then go around and allocate a new one */
91 fd
->lower
= fd
->higher
= NULL
;
92 fd
->identity
= PR_NSPR_IO_LAYER
;
93 memset(fd
->secret
, 0, sizeof(PRFilePrivate
));
97 fd
= PR_NEW(PRFileDesc
);
99 fd
->secret
= PR_NEW(PRFilePrivate
);
100 if (NULL
== fd
->secret
) {
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.
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
;
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
;
130 PR_ASSERT(NULL
== _pr_fd_cache
.tail
->higher
);
131 _pr_fd_cache
.tail
->higher
= fd
;
132 _pr_fd_cache
.tail
= fd
; /* new value */
134 fd
->higher
= NULL
; /* always so */
135 _pr_fd_cache
.count
+= 1; /* count the new entry */
136 PR_Unlock(_pr_fd_cache
.ml
);
145 PR_IMPLEMENT(PRStatus
) PR_SetFDCacheSize(PRIntn low
, PRIntn high
) {
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.
151 if (!_pr_initialized
) {
152 _PR_ImplicitInitialization();
156 low
= high
; /* sanity check the params */
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
);
164 } /* PR_SetFDCacheSize */
166 void _PR_InitFdCache(void) {
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.
173 const char* low
= PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW");
174 const char* high
= PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH");
177 ** _low is allowed to be zero, _high is not.
178 ** If _high is zero, we're not doing the caching.
181 _pr_fd_cache
.limit_low
= 0;
183 _pr_fd_cache
.limit_high
= FD_SETSIZE
;
185 _pr_fd_cache
.limit_high
= 0;
186 #endif /* defined(DEBUG) */
189 _pr_fd_cache
.limit_low
= atoi(low
);
192 _pr_fd_cache
.limit_high
= atoi(high
);
195 if (_pr_fd_cache
.limit_low
< 0) {
196 _pr_fd_cache
.limit_low
= 0;
198 if (_pr_fd_cache
.limit_low
> FD_SETSIZE
) {
199 _pr_fd_cache
.limit_low
= FD_SETSIZE
;
202 if (_pr_fd_cache
.limit_high
> FD_SETSIZE
) {
203 _pr_fd_cache
.limit_high
= FD_SETSIZE
;
206 if (_pr_fd_cache
.limit_high
< _pr_fd_cache
.limit_low
) {
207 _pr_fd_cache
.limit_high
= _pr_fd_cache
.limit_low
;
210 _pr_fd_cache
.ml
= PR_NewLock();
211 PR_ASSERT(NULL
!= _pr_fd_cache
.ml
);
213 } /* _PR_InitFdCache */
215 void _PR_CleanupFdCache(void) {
216 PRFileDesc
*fd
, *next
;
218 for (fd
= _pr_fd_cache
.head
; fd
!= NULL
; fd
= next
) {
220 PR_DELETE(fd
->secret
);
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 */