Mercurial > hg > nginx
annotate src/core/ngx_open_file_cache.c @ 9230:e14debe728b0 radix_with_skip
Closed the radix_with_skip branch.
The radix_with_skip branch is an archive of an experiment did in 2008,
and it is no longer relevant. It is now closed to avoid cluttering of
the branches list. If needed, closed branches still can be seen with
"hg branches --closed".
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sat, 23 Mar 2024 04:30:45 +0300 |
parents | 987831d73bd8 |
children | 225fa4abd76f |
rev | line source |
---|---|
1453 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 | |
11 | |
12 /* | |
13 * open file cache caches | |
14 * open file handles with stat() info; | |
15 * directories stat() info; | |
16 * files and directories errors: not found, access denied, etc. | |
17 */ | |
18 | |
19 | |
20 static void ngx_open_file_cache_cleanup(void *data); | |
1775 | 21 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, |
22 ngx_log_t *log); | |
23 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, | |
24 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); | |
1453 | 25 static void ngx_open_file_cleanup(void *data); |
26 static void ngx_close_cached_file(ngx_open_file_cache_t *cache, | |
1772 | 27 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log); |
1775 | 28 static void ngx_open_file_del_event(ngx_cached_open_file_t *file); |
1453 | 29 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, |
30 ngx_uint_t n, ngx_log_t *log); | |
31 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
32 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); | |
1775 | 33 static ngx_cached_open_file_t * |
34 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
35 uint32_t hash); | |
1453 | 36 static void ngx_open_file_cache_remove(ngx_event_t *ev); |
37 | |
38 | |
39 ngx_open_file_cache_t * | |
40 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive) | |
41 { | |
42 ngx_pool_cleanup_t *cln; | |
43 ngx_open_file_cache_t *cache; | |
44 | |
45 cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t)); | |
46 if (cache == NULL) { | |
47 return NULL; | |
48 } | |
49 | |
1761 | 50 ngx_rbtree_init(&cache->rbtree, &cache->sentinel, |
1743
4fc402c3ec73
optimize rbtree initialization and insert
Igor Sysoev <igor@sysoev.ru>
parents:
1458
diff
changeset
|
51 ngx_open_file_cache_rbtree_insert_value); |
1453 | 52 |
1765 | 53 ngx_queue_init(&cache->expire_queue); |
54 | |
1453 | 55 cache->current = 0; |
56 cache->max = max; | |
57 cache->inactive = inactive; | |
58 | |
59 cln = ngx_pool_cleanup_add(pool, 0); | |
60 if (cln == NULL) { | |
61 return NULL; | |
62 } | |
63 | |
64 cln->handler = ngx_open_file_cache_cleanup; | |
65 cln->data = cache; | |
66 | |
67 return cache; | |
68 } | |
69 | |
70 | |
71 static void | |
72 ngx_open_file_cache_cleanup(void *data) | |
73 { | |
74 ngx_open_file_cache_t *cache = data; | |
75 | |
1765 | 76 ngx_queue_t *q; |
1453 | 77 ngx_cached_open_file_t *file; |
78 | |
79 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | |
80 "open file cache cleanup"); | |
81 | |
82 for ( ;; ) { | |
83 | |
1765 | 84 if (ngx_queue_empty(&cache->expire_queue)) { |
1766 | 85 break; |
1453 | 86 } |
87 | |
1765 | 88 q = ngx_queue_last(&cache->expire_queue); |
89 | |
90 file = ngx_queue_data(q, ngx_cached_open_file_t, queue); | |
91 | |
92 ngx_queue_remove(q); | |
1453 | 93 |
94 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
95 | |
96 cache->current--; | |
97 | |
98 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | |
99 "delete cached open file: %s", file->name); | |
100 | |
101 if (!file->err && !file->is_dir) { | |
102 file->close = 1; | |
103 file->count = 0; | |
1772 | 104 ngx_close_cached_file(cache, file, 0, ngx_cycle->log); |
1453 | 105 |
106 } else { | |
107 ngx_free(file->name); | |
108 ngx_free(file); | |
109 } | |
110 } | |
111 | |
112 if (cache->current) { | |
113 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, | |
114 "%d items still leave in open file cache", | |
115 cache->current); | |
116 } | |
117 | |
118 if (cache->rbtree.root != cache->rbtree.sentinel) { | |
119 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, | |
120 "rbtree still is not empty in open file cache"); | |
121 | |
122 } | |
123 } | |
124 | |
125 | |
126 ngx_int_t | |
127 ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
128 ngx_open_file_info_t *of, ngx_pool_t *pool) | |
129 { | |
130 time_t now; | |
131 uint32_t hash; | |
132 ngx_int_t rc; | |
133 ngx_pool_cleanup_t *cln; | |
134 ngx_cached_open_file_t *file; | |
135 ngx_pool_cleanup_file_t *clnf; | |
136 ngx_open_file_cache_cleanup_t *ofcln; | |
137 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
138 of->fd = NGX_INVALID_FILE; |
1453 | 139 of->err = 0; |
140 | |
141 if (cache == NULL) { | |
142 | |
143 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); | |
144 if (cln == NULL) { | |
145 return NGX_ERROR; | |
146 } | |
147 | |
148 rc = ngx_open_and_stat_file(name->data, of, pool->log); | |
149 | |
150 if (rc == NGX_OK && !of->is_dir) { | |
151 cln->handler = ngx_pool_cleanup_file; | |
152 clnf = cln->data; | |
153 | |
154 clnf->fd = of->fd; | |
155 clnf->name = name->data; | |
156 clnf->log = pool->log; | |
157 } | |
158 | |
159 return rc; | |
160 } | |
161 | |
162 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); | |
163 if (cln == NULL) { | |
164 return NGX_ERROR; | |
165 } | |
166 | |
167 now = ngx_time(); | |
168 | |
1775 | 169 hash = ngx_crc32_long(name->data, name->len); |
170 | |
171 file = ngx_open_file_lookup(cache, name, hash); | |
172 | |
173 if (file) { | |
174 | |
175 file->uses++; | |
176 | |
1987 | 177 ngx_queue_remove(&file->queue); |
178 | |
1775 | 179 if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { |
180 | |
181 /* file was not used often enough to keep open */ | |
182 | |
183 rc = ngx_open_and_stat_file(name->data, of, pool->log); | |
1453 | 184 |
1775 | 185 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { |
186 goto failed; | |
187 } | |
188 | |
189 goto add_event; | |
1453 | 190 } |
191 | |
2070 | 192 if (file->use_event |
2063
67a29af877ed
initialize of.uniq in ngx_open_cached_file()
Igor Sysoev <igor@sysoev.ru>
parents:
2006
diff
changeset
|
193 || (file->event == NULL |
67a29af877ed
initialize of.uniq in ngx_open_cached_file()
Igor Sysoev <igor@sysoev.ru>
parents:
2006
diff
changeset
|
194 && (of->uniq == 0 || of->uniq == file->uniq) |
67a29af877ed
initialize of.uniq in ngx_open_cached_file()
Igor Sysoev <igor@sysoev.ru>
parents:
2006
diff
changeset
|
195 && now - file->created < of->valid)) |
1775 | 196 { |
197 if (file->err == 0) { | |
1772 | 198 |
1775 | 199 of->fd = file->fd; |
200 of->uniq = file->uniq; | |
201 of->mtime = file->mtime; | |
202 of->size = file->size; | |
1453 | 203 |
1775 | 204 of->is_dir = file->is_dir; |
205 of->is_file = file->is_file; | |
206 of->is_link = file->is_link; | |
207 of->is_exec = file->is_exec; | |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
208 of->is_directio = file->is_directio; |
1453 | 209 |
1775 | 210 if (!file->is_dir) { |
211 file->count++; | |
212 ngx_open_file_add_event(cache, file, of, pool->log); | |
1453 | 213 } |
214 | |
1775 | 215 } else { |
216 of->err = file->err; | |
217 } | |
218 | |
219 goto found; | |
220 } | |
221 | |
222 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, | |
223 "retest open file: %s, fd:%d, c:%d, e:%d", | |
224 file->name, file->fd, file->count, file->err); | |
1453 | 225 |
1775 | 226 if (file->is_dir) { |
227 | |
228 /* | |
229 * chances that directory became file are very small | |
230 * so test_dir flag allows to use a single syscall | |
231 * in ngx_file_info() instead of three syscalls | |
232 */ | |
233 | |
234 of->test_dir = 1; | |
235 } | |
236 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
237 of->fd = file->fd; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
238 of->uniq = file->uniq; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
239 |
1775 | 240 rc = ngx_open_and_stat_file(name->data, of, pool->log); |
1453 | 241 |
1775 | 242 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { |
243 goto failed; | |
244 } | |
245 | |
246 if (of->is_dir) { | |
247 | |
248 if (file->is_dir || file->err) { | |
249 goto update; | |
250 } | |
251 | |
252 /* file became directory */ | |
1453 | 253 |
1775 | 254 } else if (of->err == 0) { /* file */ |
255 | |
256 if (file->is_dir || file->err) { | |
257 goto add_event; | |
258 } | |
1453 | 259 |
2070 | 260 if (of->uniq == file->uniq) { |
1453 | 261 |
1775 | 262 file->count++; |
1453 | 263 |
1775 | 264 if (file->event) { |
265 file->use_event = 1; | |
1453 | 266 } |
267 | |
1775 | 268 goto renew; |
269 } | |
1453 | 270 |
1775 | 271 /* file was changed */ |
1453 | 272 |
1775 | 273 } else { /* error to cache */ |
1453 | 274 |
1775 | 275 if (file->err || file->is_dir) { |
276 goto update; | |
1453 | 277 } |
278 | |
1775 | 279 /* file was removed, etc. */ |
280 } | |
281 | |
282 if (file->count == 0) { | |
283 | |
284 ngx_open_file_del_event(file); | |
1453 | 285 |
1775 | 286 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { |
287 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
288 ngx_close_file_n " \"%s\" failed", | |
289 name->data); | |
290 } | |
1453 | 291 |
1775 | 292 goto add_event; |
293 } | |
294 | |
295 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
296 | |
297 cache->current--; | |
298 | |
299 file->close = 1; | |
300 | |
301 goto create; | |
1453 | 302 } |
303 | |
304 /* not found */ | |
305 | |
306 rc = ngx_open_and_stat_file(name->data, of, pool->log); | |
307 | |
308 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { | |
309 goto failed; | |
310 } | |
311 | |
312 create: | |
313 | |
314 if (cache->current >= cache->max) { | |
315 ngx_expire_old_cached_files(cache, 0, pool->log); | |
316 } | |
317 | |
318 file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); | |
319 | |
320 if (file == NULL) { | |
321 goto failed; | |
322 } | |
323 | |
324 file->name = ngx_alloc(name->len + 1, pool->log); | |
325 | |
326 if (file->name == NULL) { | |
327 ngx_free(file); | |
328 file = NULL; | |
329 goto failed; | |
330 } | |
331 | |
332 ngx_cpystrn(file->name, name->data, name->len + 1); | |
333 | |
334 file->node.key = hash; | |
335 | |
336 ngx_rbtree_insert(&cache->rbtree, &file->node); | |
337 | |
338 cache->current++; | |
339 | |
1775 | 340 file->uses = 1; |
1453 | 341 file->count = 0; |
1775 | 342 file->event = NULL; |
343 | |
344 add_event: | |
345 | |
346 ngx_open_file_add_event(cache, file, of, pool->log); | |
1453 | 347 |
348 update: | |
349 | |
350 file->fd = of->fd; | |
351 file->err = of->err; | |
352 | |
353 if (of->err == 0) { | |
354 file->uniq = of->uniq; | |
355 file->mtime = of->mtime; | |
356 file->size = of->size; | |
357 | |
358 file->close = 0; | |
359 | |
360 file->is_dir = of->is_dir; | |
361 file->is_file = of->is_file; | |
362 file->is_link = of->is_link; | |
363 file->is_exec = of->is_exec; | |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
364 file->is_directio = of->is_directio; |
1453 | 365 |
366 if (!of->is_dir) { | |
367 file->count++; | |
368 } | |
369 } | |
370 | |
371 renew: | |
372 | |
373 file->created = now; | |
374 | |
375 found: | |
376 | |
377 file->accessed = now; | |
378 | |
1765 | 379 ngx_queue_insert_head(&cache->expire_queue, &file->queue); |
1453 | 380 |
1772 | 381 ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, |
382 "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", | |
383 file->name, file->fd, file->count, file->err, file->uses); | |
1453 | 384 |
385 if (of->err == 0) { | |
386 | |
387 if (!of->is_dir) { | |
388 cln->handler = ngx_open_file_cleanup; | |
389 ofcln = cln->data; | |
390 | |
391 ofcln->cache = cache; | |
392 ofcln->file = file; | |
1772 | 393 ofcln->min_uses = of->min_uses; |
1453 | 394 ofcln->log = pool->log; |
395 } | |
396 | |
397 return NGX_OK; | |
398 } | |
399 | |
400 return NGX_ERROR; | |
401 | |
402 failed: | |
403 | |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
404 if (file) { |
1453 | 405 ngx_rbtree_delete(&cache->rbtree, &file->node); |
406 | |
407 cache->current--; | |
408 | |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
409 if (file->count == 0) { |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
410 |
2006
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
411 if (file->fd != NGX_INVALID_FILE) { |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
412 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
413 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
414 ngx_close_file_n " \"%s\" failed", |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
415 file->name); |
2006
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
416 } |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
417 } |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
418 |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
419 ngx_free(file->name); |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
420 ngx_free(file); |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
421 |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
422 } else { |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
423 file->close = 1; |
1453 | 424 } |
425 } | |
426 | |
427 if (of->fd != NGX_INVALID_FILE) { | |
428 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { | |
429 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
430 ngx_close_file_n " \"%s\" failed", name->data); | |
431 } | |
432 } | |
433 | |
434 return NGX_ERROR; | |
435 } | |
436 | |
437 | |
438 static ngx_int_t | |
439 ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) | |
440 { | |
441 ngx_fd_t fd; | |
442 ngx_file_info_t fi; | |
443 | |
2070 | 444 if (of->fd != NGX_INVALID_FILE) { |
1453 | 445 |
446 if (ngx_file_info(name, &fi) == -1) { | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
447 goto failed; |
1453 | 448 } |
449 | |
2070 | 450 if (of->uniq == ngx_file_uniq(&fi)) { |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
451 goto done; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
452 } |
1453 | 453 |
2070 | 454 } else if (of->test_dir) { |
455 | |
456 if (ngx_file_info(name, &fi) == -1) { | |
457 goto failed; | |
458 } | |
459 | |
460 if (of->is_dir) { | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
461 goto done; |
1453 | 462 } |
463 } | |
464 | |
2072 | 465 if (!of->log) { |
466 fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); | |
467 | |
468 } else { | |
469 fd = ngx_open_file(name, NGX_FILE_RDWR, | |
470 NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, | |
471 NGX_FILE_DEFAULT_ACCESS); | |
472 } | |
1453 | 473 |
474 if (fd == NGX_INVALID_FILE) { | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
475 goto failed; |
1453 | 476 } |
477 | |
478 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { | |
479 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, | |
480 ngx_fd_info_n " \"%s\" failed", name); | |
481 | |
482 if (ngx_close_file(fd) == NGX_FILE_ERROR) { | |
483 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
484 ngx_close_file_n " \"%s\" failed", name); | |
485 } | |
486 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
487 of->fd = NGX_INVALID_FILE; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
488 |
1453 | 489 return NGX_ERROR; |
490 } | |
491 | |
492 if (ngx_is_dir(&fi)) { | |
493 if (ngx_close_file(fd) == NGX_FILE_ERROR) { | |
494 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
495 ngx_close_file_n " \"%s\" failed", name); | |
496 } | |
497 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
498 of->fd = NGX_INVALID_FILE; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
499 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
500 } else { |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
501 of->fd = fd; |
2129 | 502 |
503 if (of->directio <= ngx_file_size(&fi)) { | |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
504 if (ngx_directio_on(fd) == -1) { |
2129 | 505 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
506 ngx_directio_on_n " \"%s\" failed", name); |
2231
8564129d49b6
*) handle unaligned file part for directio
Igor Sysoev <igor@sysoev.ru>
parents:
2129
diff
changeset
|
507 |
8564129d49b6
*) handle unaligned file part for directio
Igor Sysoev <igor@sysoev.ru>
parents:
2129
diff
changeset
|
508 } else { |
8564129d49b6
*) handle unaligned file part for directio
Igor Sysoev <igor@sysoev.ru>
parents:
2129
diff
changeset
|
509 of->is_directio = 1; |
2129 | 510 } |
511 } | |
1453 | 512 } |
513 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
514 done: |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
515 |
1453 | 516 of->uniq = ngx_file_uniq(&fi); |
517 of->mtime = ngx_file_mtime(&fi); | |
518 of->size = ngx_file_size(&fi); | |
519 of->is_dir = ngx_is_dir(&fi); | |
520 of->is_file = ngx_is_file(&fi); | |
521 of->is_link = ngx_is_link(&fi); | |
522 of->is_exec = ngx_is_exec(&fi); | |
523 | |
524 return NGX_OK; | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
525 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
526 failed: |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
527 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
528 of->fd = NGX_INVALID_FILE; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
529 of->err = ngx_errno; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
530 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
531 return NGX_ERROR; |
1453 | 532 } |
533 | |
534 | |
1775 | 535 /* |
536 * we ignore any possible event setting error and | |
537 * fallback to usual periodic file retests | |
538 */ | |
539 | |
540 static void | |
541 ngx_open_file_add_event(ngx_open_file_cache_t *cache, | |
542 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log) | |
543 { | |
544 ngx_open_file_cache_event_t *fev; | |
545 | |
546 if (!(ngx_event_flags & NGX_USE_VNODE_EVENT) | |
547 || !of->events | |
548 || file->event | |
549 || of->fd == NGX_INVALID_FILE | |
550 || file->uses < of->min_uses) | |
551 { | |
552 return; | |
553 } | |
554 | |
2070 | 555 file->use_event = 0; |
556 | |
1775 | 557 file->event = ngx_calloc(sizeof(ngx_event_t), log); |
558 if (file->event== NULL) { | |
559 return; | |
560 } | |
561 | |
562 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log); | |
563 if (fev == NULL) { | |
564 ngx_free(file->event); | |
565 file->event = NULL; | |
566 return; | |
567 } | |
568 | |
569 fev->fd = of->fd; | |
570 fev->file = file; | |
571 fev->cache = cache; | |
572 | |
573 file->event->handler = ngx_open_file_cache_remove; | |
574 file->event->data = fev; | |
575 | |
576 /* | |
577 * although vnode event may be called while ngx_cycle->poll | |
578 * destruction, however, cleanup procedures are run before any | |
579 * memory freeing and events will be canceled. | |
580 */ | |
581 | |
582 file->event->log = ngx_cycle->log; | |
583 | |
584 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT) | |
585 != NGX_OK) | |
586 { | |
587 ngx_free(file->event->data); | |
588 ngx_free(file->event); | |
589 file->event = NULL; | |
590 return; | |
591 } | |
592 | |
593 /* | |
2071 | 594 * we do not set file->use_event here because there may be a race |
595 * condition: a file may be deleted between opening the file and | |
596 * adding event, so we rely upon event notification only after | |
597 * one file revalidation on next file access | |
1775 | 598 */ |
599 | |
600 return; | |
601 } | |
602 | |
603 | |
1453 | 604 static void |
605 ngx_open_file_cleanup(void *data) | |
606 { | |
607 ngx_open_file_cache_cleanup_t *c = data; | |
608 | |
609 c->file->count--; | |
610 | |
1772 | 611 ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log); |
1453 | 612 |
613 /* drop one or two expired open files */ | |
614 ngx_expire_old_cached_files(c->cache, 1, c->log); | |
615 } | |
616 | |
617 | |
618 static void | |
619 ngx_close_cached_file(ngx_open_file_cache_t *cache, | |
1772 | 620 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log) |
1453 | 621 { |
1772 | 622 ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0, |
623 "close cached open file: %s, fd:%d, c:%d, u:%d, %d", | |
624 file->name, file->fd, file->count, file->uses, file->close); | |
1453 | 625 |
626 if (!file->close) { | |
627 | |
628 file->accessed = ngx_time(); | |
629 | |
1765 | 630 ngx_queue_remove(&file->queue); |
1453 | 631 |
1765 | 632 ngx_queue_insert_head(&cache->expire_queue, &file->queue); |
1453 | 633 |
1772 | 634 if (file->uses >= min_uses || file->count) { |
635 return; | |
636 } | |
1453 | 637 } |
638 | |
1775 | 639 ngx_open_file_del_event(file); |
1453 | 640 |
641 if (file->count) { | |
642 return; | |
643 } | |
644 | |
1772 | 645 if (file->fd != NGX_INVALID_FILE) { |
646 | |
647 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { | |
648 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
649 ngx_close_file_n " \"%s\" failed", file->name); | |
650 } | |
651 | |
652 file->fd = NGX_INVALID_FILE; | |
653 } | |
654 | |
655 if (!file->close) { | |
656 return; | |
1453 | 657 } |
658 | |
659 ngx_free(file->name); | |
660 ngx_free(file); | |
661 } | |
662 | |
663 | |
664 static void | |
1775 | 665 ngx_open_file_del_event(ngx_cached_open_file_t *file) |
666 { | |
667 if (file->event == NULL) { | |
668 return; | |
669 } | |
670 | |
671 (void) ngx_del_event(file->event, NGX_VNODE_EVENT, | |
672 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT); | |
673 | |
674 ngx_free(file->event->data); | |
675 ngx_free(file->event); | |
676 file->event = NULL; | |
677 file->use_event = 0; | |
678 } | |
679 | |
680 | |
681 static void | |
1453 | 682 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, |
683 ngx_log_t *log) | |
684 { | |
685 time_t now; | |
1765 | 686 ngx_queue_t *q; |
1453 | 687 ngx_cached_open_file_t *file; |
688 | |
689 now = ngx_time(); | |
690 | |
691 /* | |
692 * n == 1 deletes one or two inactive files | |
693 * n == 0 deletes least recently used file by force | |
694 * and one or two inactive files | |
695 */ | |
696 | |
697 while (n < 3) { | |
698 | |
1765 | 699 if (ngx_queue_empty(&cache->expire_queue)) { |
1453 | 700 return; |
701 } | |
702 | |
1765 | 703 q = ngx_queue_last(&cache->expire_queue); |
704 | |
705 file = ngx_queue_data(q, ngx_cached_open_file_t, queue); | |
706 | |
1453 | 707 if (n++ != 0 && now - file->accessed <= cache->inactive) { |
708 return; | |
709 } | |
710 | |
1765 | 711 ngx_queue_remove(q); |
1453 | 712 |
713 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
714 | |
715 cache->current--; | |
716 | |
717 ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, | |
718 "expire cached open file: %s", file->name); | |
719 | |
720 if (!file->err && !file->is_dir) { | |
721 file->close = 1; | |
1772 | 722 ngx_close_cached_file(cache, file, 0, log); |
1453 | 723 |
724 } else { | |
725 ngx_free(file->name); | |
726 ngx_free(file); | |
727 } | |
728 } | |
729 } | |
730 | |
731 | |
732 static void | |
733 ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
734 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) | |
735 { | |
736 ngx_rbtree_node_t **p; | |
737 ngx_cached_open_file_t *file, *file_temp; | |
738 | |
739 for ( ;; ) { | |
740 | |
741 if (node->key < temp->key) { | |
742 | |
743 p = &temp->left; | |
744 | |
745 } else if (node->key > temp->key) { | |
746 | |
747 p = &temp->right; | |
748 | |
749 } else { /* node->key == temp->key */ | |
750 | |
751 file = (ngx_cached_open_file_t *) node; | |
752 file_temp = (ngx_cached_open_file_t *) temp; | |
753 | |
754 p = (ngx_strcmp(file->name, file_temp->name) < 0) | |
755 ? &temp->left : &temp->right; | |
756 } | |
757 | |
758 if (*p == sentinel) { | |
759 break; | |
760 } | |
761 | |
762 temp = *p; | |
763 } | |
764 | |
765 *p = node; | |
766 node->parent = temp; | |
767 node->left = sentinel; | |
768 node->right = sentinel; | |
769 ngx_rbt_red(node); | |
770 } | |
771 | |
772 | |
1775 | 773 static ngx_cached_open_file_t * |
774 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
775 uint32_t hash) | |
776 { | |
777 ngx_int_t rc; | |
778 ngx_rbtree_node_t *node, *sentinel; | |
779 ngx_cached_open_file_t *file; | |
780 | |
781 node = cache->rbtree.root; | |
782 sentinel = cache->rbtree.sentinel; | |
783 | |
784 while (node != sentinel) { | |
785 | |
786 if (hash < node->key) { | |
787 node = node->left; | |
788 continue; | |
789 } | |
790 | |
791 if (hash > node->key) { | |
792 node = node->right; | |
793 continue; | |
794 } | |
795 | |
796 /* hash == node->key */ | |
797 | |
798 do { | |
799 file = (ngx_cached_open_file_t *) node; | |
800 | |
801 rc = ngx_strcmp(name->data, file->name); | |
802 | |
803 if (rc == 0) { | |
804 return file; | |
805 } | |
806 | |
807 node = (rc < 0) ? node->left : node->right; | |
808 | |
809 } while (node != sentinel && hash == node->key); | |
810 | |
811 break; | |
812 } | |
813 | |
814 return NULL; | |
815 } | |
816 | |
817 | |
1453 | 818 static void |
819 ngx_open_file_cache_remove(ngx_event_t *ev) | |
820 { | |
821 ngx_cached_open_file_t *file; | |
822 ngx_open_file_cache_event_t *fev; | |
823 | |
824 fev = ev->data; | |
825 file = fev->file; | |
826 | |
1765 | 827 ngx_queue_remove(&file->queue); |
1453 | 828 |
829 ngx_rbtree_delete(&fev->cache->rbtree, &file->node); | |
830 | |
831 fev->cache->current--; | |
832 | |
833 /* NGX_ONESHOT_EVENT was already deleted */ | |
834 file->event = NULL; | |
2070 | 835 file->use_event = 0; |
1453 | 836 |
837 file->close = 1; | |
838 | |
1772 | 839 ngx_close_cached_file(fev->cache, file, 0, ev->log); |
1453 | 840 |
841 /* free memory only when fev->cache and fev->file are already not needed */ | |
842 | |
843 ngx_free(ev->data); | |
844 ngx_free(ev); | |
845 } |