Klaus Demo nginx / d92bee5
open file cache Igor Sysoev 14 years ago
4 changed file(s) with 834 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
2828 src/core/ngx_connection.h \
2929 src/core/ngx_cycle.h \
3030 src/core/ngx_conf_file.h \
31 src/core/ngx_open_file_cache.h \
3132 src/core/ngx_garbage_collector.h"
3233
3334
5455 src/core/ngx_spinlock.c \
5556 src/core/ngx_cpuinfo.c \
5657 src/core/ngx_conf_file.c \
58 src/core/ngx_open_file_cache.c \
5759 src/core/ngx_garbage_collector.c"
5860
5961
7070 #endif
7171 #include <ngx_process_cycle.h>
7272 #include <ngx_conf_file.h>
73 #include <ngx_open_file_cache.h>
7374 #include <ngx_os.h>
7475 #include <ngx_connection.h>
7576
0
1 /*
2 * Copyright (C) Igor Sysoev
3 */
4
5
6 #include <ngx_config.h>
7 #include <ngx_core.h>
8 #include <ngx_event.h>
9
10
11 /*
12 * open file cache caches
13 * open file handles with stat() info;
14 * directories stat() info;
15 * files and directories errors: not found, access denied, etc.
16 */
17
18
19 static void ngx_open_file_cache_cleanup(void *data);
20 static void ngx_open_file_cleanup(void *data);
21 static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
22 ngx_cached_open_file_t *file, ngx_log_t *log);
23 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
24 ngx_log_t *log);
25 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
26 ngx_uint_t n, ngx_log_t *log);
27 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
28 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
29 static void ngx_open_file_cache_remove(ngx_event_t *ev);
30
31
32 ngx_open_file_cache_t *
33 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
34 {
35 ngx_rbtree_node_t *sentinel;
36 ngx_pool_cleanup_t *cln;
37 ngx_open_file_cache_t *cache;
38
39 cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
40 if (cache == NULL) {
41 return NULL;
42 }
43
44 cache->list_head.prev = NULL;
45 cache->list_head.next = &cache->list_tail;
46
47 cache->list_tail.prev = &cache->list_head;
48 cache->list_tail.next = NULL;
49
50 sentinel = ngx_palloc(pool, sizeof(ngx_rbtree_node_t));
51 if (sentinel == NULL) {
52 return NULL;
53 }
54
55 ngx_rbtree_sentinel_init(sentinel);
56
57 cache->rbtree.root = sentinel;
58 cache->rbtree.sentinel = sentinel;
59 cache->rbtree.insert = ngx_open_file_cache_rbtree_insert_value;
60
61 cache->current = 0;
62 cache->max = max;
63 cache->inactive = inactive;
64
65 cln = ngx_pool_cleanup_add(pool, 0);
66 if (cln == NULL) {
67 return NULL;
68 }
69
70 cln->handler = ngx_open_file_cache_cleanup;
71 cln->data = cache;
72
73 return cache;
74 }
75
76
77 static void
78 ngx_open_file_cache_cleanup(void *data)
79 {
80 ngx_open_file_cache_t *cache = data;
81
82 ngx_cached_open_file_t *file;
83
84 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
85 "open file cache cleanup");
86
87 for ( ;; ) {
88
89 file = cache->list_tail.prev;
90
91 if (file == &cache->list_head) {
92 break;
93 }
94
95 file->next->prev = file->prev;
96 file->prev->next = file->next;
97
98 ngx_rbtree_delete(&cache->rbtree, &file->node);
99
100 cache->current--;
101
102 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
103 "delete cached open file: %s", file->name);
104
105 if (!file->err && !file->is_dir) {
106 file->close = 1;
107 file->count = 0;
108 ngx_close_cached_file(cache, file, ngx_cycle->log);
109
110 } else {
111 ngx_free(file->name);
112 ngx_free(file);
113 }
114 }
115
116 if (cache->current) {
117 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
118 "%d items still leave in open file cache",
119 cache->current);
120 }
121
122 if (cache->rbtree.root != cache->rbtree.sentinel) {
123 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
124 "rbtree still is not empty in open file cache");
125
126 }
127 }
128
129
130 ngx_int_t
131 ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
132 ngx_open_file_info_t *of, ngx_pool_t *pool)
133 {
134 time_t now;
135 uint32_t hash;
136 ngx_int_t rc;
137 ngx_rbtree_node_t *node, *sentinel;
138 ngx_pool_cleanup_t *cln;
139 ngx_cached_open_file_t *file;
140 ngx_pool_cleanup_file_t *clnf;
141 ngx_open_file_cache_event_t *fev;
142 ngx_open_file_cache_cleanup_t *ofcln;
143
144 of->err = 0;
145
146 if (cache == NULL) {
147
148 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
149 if (cln == NULL) {
150 return NGX_ERROR;
151 }
152
153 rc = ngx_open_and_stat_file(name->data, of, pool->log);
154
155 if (rc == NGX_OK && !of->is_dir) {
156 cln->handler = ngx_pool_cleanup_file;
157 clnf = cln->data;
158
159 clnf->fd = of->fd;
160 clnf->name = name->data;
161 clnf->log = pool->log;
162 }
163
164 return rc;
165 }
166
167 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
168 if (cln == NULL) {
169 return NGX_ERROR;
170 }
171
172 hash = ngx_crc32_long(name->data, name->len);
173
174 node = cache->rbtree.root;
175 sentinel = cache->rbtree.sentinel;
176
177 now = ngx_time();
178
179 while (node != sentinel) {
180
181 if (hash < node->key) {
182 node = node->left;
183 continue;
184 }
185
186 if (hash > node->key) {
187 node = node->right;
188 continue;
189 }
190
191 /* hash == node->key */
192
193 do {
194 file = (ngx_cached_open_file_t *) node;
195
196 rc = ngx_strcmp(name->data, file->name);
197
198 if (rc == 0) {
199
200 file->next->prev = file->prev;
201 file->prev->next = file->next;
202
203 if (file->event || now - file->created < of->retest) {
204 if (file->err == 0) {
205 of->fd = file->fd;
206 of->uniq = file->uniq;
207 of->mtime = file->mtime;
208 of->size = file->size;
209
210 of->is_dir = file->is_dir;
211 of->is_file = file->is_file;
212 of->is_link = file->is_link;
213 of->is_exec = file->is_exec;
214
215 if (!file->is_dir) {
216 file->count++;
217 }
218
219 } else {
220 of->err = file->err;
221 }
222
223 goto found;
224 }
225
226 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
227 "retest open file: %s, fd:%d, c:%d, e:%d",
228 file->name, file->fd, file->count, file->err);
229
230 if (file->is_dir) {
231
232 /*
233 * chances that directory became file are very small
234 * so test_dir flag allows to use a single ngx_file_info()
235 * syscall instead of three syscalls
236 */
237
238 of->test_dir = 1;
239 }
240
241 rc = ngx_open_and_stat_file(name->data, of, pool->log);
242
243 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
244 goto failed;
245 }
246
247 if (of->is_dir) {
248 if (file->is_dir || file->err) {
249 goto update;
250 }
251
252 /* file became directory */
253
254 } else if (of->err == 0) { /* file */
255
256 if (file->is_dir || file->err) {
257 goto update;
258 }
259
260 if (of->uniq == file->uniq
261 && of->mtime == file->mtime
262 && of->size == file->size)
263 {
264 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
265 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
266 ngx_close_file_n " \"%s\" failed",
267 name->data);
268 }
269
270 of->fd = file->fd;
271 file->count++;
272
273 goto renew;
274 }
275
276 /* file was changed */
277
278 } else { /* error to cache */
279
280 if (file->err || file->is_dir) {
281 goto update;
282 }
283
284 /* file was removed, etc. */
285 }
286
287 if (file->count == 0) {
288 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
289 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
290 ngx_close_file_n " \"%s\" failed",
291 name->data);
292 }
293
294 goto update;
295 }
296
297 ngx_rbtree_delete(&cache->rbtree, &file->node);
298
299 cache->current--;
300
301 file->close = 1;
302
303 goto create;
304 }
305
306 node = (rc < 0) ? node->left : node->right;
307
308 } while (node != sentinel && hash == node->key);
309
310 break;
311 }
312
313 /* not found */
314
315 file = NULL;
316
317 rc = ngx_open_and_stat_file(name->data, of, pool->log);
318
319 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
320 goto failed;
321 }
322
323 create:
324
325 if (cache->current >= cache->max) {
326 ngx_expire_old_cached_files(cache, 0, pool->log);
327 }
328
329 file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
330
331 if (file == NULL) {
332 goto failed;
333 }
334
335 file->name = ngx_alloc(name->len + 1, pool->log);
336
337 if (file->name == NULL) {
338 ngx_free(file);
339 file = NULL;
340 goto failed;
341 }
342
343 ngx_cpystrn(file->name, name->data, name->len + 1);
344
345 file->node.key = hash;
346
347 ngx_rbtree_insert(&cache->rbtree, &file->node);
348
349 cache->current++;
350
351 file->count = 0;
352
353 update:
354
355 if (ngx_event_flags & NGX_USE_VNODE_EVENT && of->fd != NGX_INVALID_FILE) {
356
357 file->event = ngx_calloc(sizeof(ngx_event_t), pool->log);
358 if (file->event== NULL) {
359 goto failed;
360 }
361
362 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), pool->log);
363 if (fev == NULL) {
364 goto failed;
365 }
366
367 fev->fd = of->fd;
368 fev->file = file;
369 fev->cache = cache;
370
371 file->event->handler = ngx_open_file_cache_remove;
372 file->event->data = fev;
373
374 /*
375 * although vnode event may be called while ngx_cycle->poll
376 * destruction; however, cleanup procedures are run before any
377 * memory freeing and events will be canceled.
378 */
379
380 file->event->log = ngx_cycle->log;
381
382 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
383 != NGX_OK)
384 {
385 ngx_free(file->event->data);
386 ngx_free(file->event);
387 goto failed;
388 }
389
390 } else {
391 file->event = NULL;
392 }
393
394 file->fd = of->fd;
395 file->err = of->err;
396
397 if (of->err == 0) {
398 file->uniq = of->uniq;
399 file->mtime = of->mtime;
400 file->size = of->size;
401
402 file->close = 0;
403
404 file->is_dir = of->is_dir;
405 file->is_file = of->is_file;
406 file->is_link = of->is_link;
407 file->is_exec = of->is_exec;
408
409 if (!of->is_dir) {
410 file->count++;
411 }
412 }
413
414 renew:
415
416 file->created = now;
417
418 found:
419
420 file->accessed = now;
421
422 /* add to the inactive list head */
423
424 file->next = cache->list_head.next;
425 file->next->prev = file;
426 file->prev = &cache->list_head;
427 cache->list_head.next = file;
428
429 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
430 "cached open file: %s, fd:%d, c:%d, e:%d",
431 file->name, file->fd, file->count, file->err);
432
433 if (of->err == 0) {
434
435 if (!of->is_dir) {
436 cln->handler = ngx_open_file_cleanup;
437 ofcln = cln->data;
438
439 ofcln->cache = cache;
440 ofcln->file = file;
441 ofcln->log = pool->log;
442 }
443
444 return NGX_OK;
445 }
446
447 return NGX_ERROR;
448
449 failed:
450
451 if (file && file->count == 0) {
452 ngx_rbtree_delete(&cache->rbtree, &file->node);
453
454 cache->current--;
455
456 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
457 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
458 ngx_close_file_n " \"%s\" failed", file->name);
459 }
460
461 ngx_free(file->name);
462 ngx_free(file);
463 }
464
465 if (of->fd != NGX_INVALID_FILE) {
466 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
467 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
468 ngx_close_file_n " \"%s\" failed", name->data);
469 }
470 }
471
472 return NGX_ERROR;
473 }
474
475
476 static ngx_int_t
477 ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
478 {
479 ngx_fd_t fd;
480 ngx_file_info_t fi;
481
482 of->fd = NGX_INVALID_FILE;
483
484 if (of->test_dir) {
485
486 if (ngx_file_info(name, &fi) == -1) {
487 of->err = ngx_errno;
488
489 return NGX_ERROR;
490 }
491
492 of->uniq = ngx_file_uniq(&fi);
493 of->mtime = ngx_file_mtime(&fi);
494 of->size = ngx_file_size(&fi);
495 of->is_dir = ngx_is_dir(&fi);
496 of->is_file = ngx_is_file(&fi);
497 of->is_link = ngx_is_link(&fi);
498 of->is_exec = ngx_is_exec(&fi);
499
500 if (of->is_dir) {
501 return NGX_OK;
502 }
503 }
504
505 fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
506
507 if (fd == NGX_INVALID_FILE) {
508 of->err = ngx_errno;
509 return NGX_ERROR;
510 }
511
512 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
513 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
514 ngx_fd_info_n " \"%s\" failed", name);
515
516 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
517 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
518 ngx_close_file_n " \"%s\" failed", name);
519 }
520
521 return NGX_ERROR;
522 }
523
524 if (ngx_is_dir(&fi)) {
525 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
526 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
527 ngx_close_file_n " \"%s\" failed", name);
528 }
529
530 fd = NGX_INVALID_FILE;
531 }
532
533 of->fd = fd;
534 of->uniq = ngx_file_uniq(&fi);
535 of->mtime = ngx_file_mtime(&fi);
536 of->size = ngx_file_size(&fi);
537 of->is_dir = ngx_is_dir(&fi);
538 of->is_file = ngx_is_file(&fi);
539 of->is_link = ngx_is_link(&fi);
540 of->is_exec = ngx_is_exec(&fi);
541
542 return NGX_OK;
543 }
544
545
546 static void
547 ngx_open_file_cleanup(void *data)
548 {
549 ngx_open_file_cache_cleanup_t *c = data;
550
551 c->file->count--;
552
553 ngx_close_cached_file(c->cache, c->file, c->log);
554
555 /* drop one or two expired open files */
556 ngx_expire_old_cached_files(c->cache, 1, c->log);
557 }
558
559
560 static void
561 ngx_close_cached_file(ngx_open_file_cache_t *cache,
562 ngx_cached_open_file_t *file, ngx_log_t *log)
563 {
564 ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0,
565 "close cached open file: %s, fd:%d, c:%d, %d",
566 file->name, file->fd, file->count, file->close);
567
568 if (!file->close) {
569
570 file->accessed = ngx_time();
571
572 if (cache->list_head.next != file) {
573
574 /* delete from inactive list */
575
576 file->next->prev = file->prev;
577 file->prev->next = file->next;
578
579 /* add to the inactive list head */
580
581 file->next = cache->list_head.next;
582 file->next->prev = file;
583 file->prev = &cache->list_head;
584 cache->list_head.next = file;
585 }
586
587 return;
588 }
589
590 if (file->event) {
591 (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
592 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
593
594 ngx_free(file->event->data);
595 ngx_free(file->event);
596 file->event = NULL;
597 }
598
599 if (file->count) {
600 return;
601 }
602
603 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
604 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
605 ngx_close_file_n " \"%s\" failed", file->name);
606 }
607
608 ngx_free(file->name);
609 ngx_free(file);
610 }
611
612
613 static void
614 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
615 ngx_log_t *log)
616 {
617 time_t now;
618 ngx_cached_open_file_t *file;
619
620 now = ngx_time();
621
622 /*
623 * n == 1 deletes one or two inactive files
624 * n == 0 deletes least recently used file by force
625 * and one or two inactive files
626 */
627
628 while (n < 3) {
629
630 file = cache->list_tail.prev;
631
632 if (file == &cache->list_head) {
633 return;
634 }
635
636 if (n++ != 0 && now - file->accessed <= cache->inactive) {
637 return;
638 }
639
640 file->next->prev = file->prev;
641 file->prev->next = file->next;
642
643 ngx_rbtree_delete(&cache->rbtree, &file->node);
644
645 cache->current--;
646
647 ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
648 "expire cached open file: %s", file->name);
649
650 if (!file->err && !file->is_dir) {
651 file->close = 1;
652 ngx_close_cached_file(cache, file, log);
653
654 } else {
655 ngx_free(file->name);
656 ngx_free(file);
657 }
658 }
659 }
660
661
662 static void
663 ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
664 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
665 {
666 ngx_rbtree_node_t **p;
667 ngx_cached_open_file_t *file, *file_temp;
668
669 for ( ;; ) {
670
671 if (node->key < temp->key) {
672
673 p = &temp->left;
674
675 } else if (node->key > temp->key) {
676
677 p = &temp->right;
678
679 } else { /* node->key == temp->key */
680
681 file = (ngx_cached_open_file_t *) node;
682 file_temp = (ngx_cached_open_file_t *) temp;
683
684 p = (ngx_strcmp(file->name, file_temp->name) < 0)
685 ? &temp->left : &temp->right;
686 }
687
688 if (*p == sentinel) {
689 break;
690 }
691
692 temp = *p;
693 }
694
695 *p = node;
696 node->parent = temp;
697 node->left = sentinel;
698 node->right = sentinel;
699 ngx_rbt_red(node);
700 }
701
702
703 static void
704 ngx_open_file_cache_remove(ngx_event_t *ev)
705 {
706 ngx_cached_open_file_t *file;
707 ngx_open_file_cache_event_t *fev;
708
709 fev = ev->data;
710 file = fev->file;
711
712 file->next->prev = file->prev;
713 file->prev->next = file->next;
714
715 ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
716
717 fev->cache->current--;
718
719 /* NGX_ONESHOT_EVENT was already deleted */
720 file->event = NULL;
721
722 file->close = 1;
723
724 ngx_close_cached_file(fev->cache, file, ev->log);
725
726 /* free memory only when fev->cache and fev->file are already not needed */
727
728 ngx_free(ev->data);
729 ngx_free(ev);
730 }
0
1 /*
2 * Copyright (C) Igor Sysoev
3 */
4
5
6 #include <ngx_config.h>
7 #include <ngx_core.h>
8
9
10 #ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_
11 #define _NGX_OPEN_FILE_CACHE_H_INCLUDED_
12
13
14 typedef struct {
15 ngx_fd_t fd;
16 ngx_file_uniq_t uniq;
17 time_t mtime;
18 off_t size;
19 ngx_err_t err;
20
21 time_t retest;
22
23 unsigned test_dir:1;
24 unsigned errors:1;
25
26 unsigned is_dir:1;
27 unsigned is_file:1;
28 unsigned is_link:1;
29 unsigned is_exec:1;
30 } ngx_open_file_info_t;
31
32
33 typedef struct ngx_cached_open_file_s ngx_cached_open_file_t;
34
35 struct ngx_cached_open_file_s {
36 ngx_rbtree_node_t node;
37 ngx_cached_open_file_t *prev;
38 ngx_cached_open_file_t *next;
39
40 u_char *name;
41 time_t created;
42 time_t accessed;
43
44 ngx_fd_t fd;
45 ngx_file_uniq_t uniq;
46 time_t mtime;
47 off_t size;
48 ngx_err_t err;
49
50 unsigned count:24;
51 unsigned close:1;
52
53 unsigned is_dir:1;
54 unsigned is_file:1;
55 unsigned is_link:1;
56 unsigned is_exec:1;
57
58 ngx_event_t *event;
59 };
60
61
62 typedef struct {
63 ngx_rbtree_t rbtree;
64 ngx_cached_open_file_t list_head;
65 ngx_cached_open_file_t list_tail;
66
67 ngx_uint_t current;
68 ngx_uint_t max;
69 time_t inactive;
70 } ngx_open_file_cache_t;
71
72
73 typedef struct {
74 ngx_open_file_cache_t *cache;
75 ngx_cached_open_file_t *file;
76 ngx_log_t *log;
77 } ngx_open_file_cache_cleanup_t;
78
79
80 typedef struct {
81
82 /* ngx_connection_t stub to allow use c->fd as event ident */
83 void *data;
84 ngx_event_t *read;
85 ngx_event_t *write;
86 ngx_fd_t fd;
87
88 ngx_cached_open_file_t *file;
89 ngx_open_file_cache_t *cache;
90 } ngx_open_file_cache_event_t;
91
92
93 ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool,
94 ngx_uint_t max, time_t inactive);
95 ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
96 ngx_open_file_info_t *of, ngx_pool_t *pool);
97
98
99 #endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */