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