Mercurial > hg > nginx
comparison src/core/ngx_open_file_cache.c @ 1775:108576aef610
several fixes:
*) do not add event if file was used less than min_uses
*) do not rely upon event to avoid race conditions
*) ngx_open_file_lookup()
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 25 Dec 2007 10:46:40 +0000 |
parents | 25c93614e6b9 |
children | e829527ca810 |
comparison
equal
deleted
inserted
replaced
1774:68d21fd1dc64 | 1775:108576aef610 |
---|---|
16 * files and directories errors: not found, access denied, etc. | 16 * files and directories errors: not found, access denied, etc. |
17 */ | 17 */ |
18 | 18 |
19 | 19 |
20 static void ngx_open_file_cache_cleanup(void *data); | 20 static void ngx_open_file_cache_cleanup(void *data); |
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); | |
21 static void ngx_open_file_cleanup(void *data); | 25 static void ngx_open_file_cleanup(void *data); |
22 static void ngx_close_cached_file(ngx_open_file_cache_t *cache, | 26 static void ngx_close_cached_file(ngx_open_file_cache_t *cache, |
23 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log); | 27 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log); |
24 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, | 28 static void ngx_open_file_del_event(ngx_cached_open_file_t *file); |
25 ngx_log_t *log); | |
26 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, | 29 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, |
27 ngx_uint_t n, ngx_log_t *log); | 30 ngx_uint_t n, ngx_log_t *log); |
28 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | 31 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, |
29 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); | 32 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); |
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); | |
30 static void ngx_open_file_cache_remove(ngx_event_t *ev); | 36 static void ngx_open_file_cache_remove(ngx_event_t *ev); |
31 | 37 |
32 | 38 |
33 ngx_open_file_cache_t * | 39 ngx_open_file_cache_t * |
34 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive) | 40 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive) |
122 ngx_open_file_info_t *of, ngx_pool_t *pool) | 128 ngx_open_file_info_t *of, ngx_pool_t *pool) |
123 { | 129 { |
124 time_t now; | 130 time_t now; |
125 uint32_t hash; | 131 uint32_t hash; |
126 ngx_int_t rc; | 132 ngx_int_t rc; |
127 ngx_rbtree_node_t *node, *sentinel; | |
128 ngx_pool_cleanup_t *cln; | 133 ngx_pool_cleanup_t *cln; |
129 ngx_cached_open_file_t *file; | 134 ngx_cached_open_file_t *file; |
130 ngx_pool_cleanup_file_t *clnf; | 135 ngx_pool_cleanup_file_t *clnf; |
131 ngx_open_file_cache_event_t *fev; | |
132 ngx_open_file_cache_cleanup_t *ofcln; | 136 ngx_open_file_cache_cleanup_t *ofcln; |
133 | 137 |
134 of->err = 0; | 138 of->err = 0; |
135 | 139 |
136 if (cache == NULL) { | 140 if (cache == NULL) { |
157 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); | 161 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); |
158 if (cln == NULL) { | 162 if (cln == NULL) { |
159 return NGX_ERROR; | 163 return NGX_ERROR; |
160 } | 164 } |
161 | 165 |
166 now = ngx_time(); | |
167 | |
162 hash = ngx_crc32_long(name->data, name->len); | 168 hash = ngx_crc32_long(name->data, name->len); |
163 | 169 |
164 node = cache->rbtree.root; | 170 file = ngx_open_file_lookup(cache, name, hash); |
165 sentinel = cache->rbtree.sentinel; | 171 |
166 | 172 if (file) { |
167 now = ngx_time(); | 173 |
168 | 174 file->uses++; |
169 while (node != sentinel) { | 175 |
170 | 176 ngx_queue_remove(&file->queue); |
171 if (hash < node->key) { | 177 |
172 node = node->left; | 178 if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { |
173 continue; | 179 |
174 } | 180 /* file was not used often enough to keep open */ |
175 | 181 |
176 if (hash > node->key) { | 182 rc = ngx_open_and_stat_file(name->data, of, pool->log); |
177 node = node->right; | 183 |
178 continue; | 184 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { |
179 } | 185 goto failed; |
180 | 186 } |
181 /* hash == node->key */ | 187 |
182 | 188 goto add_event; |
183 do { | 189 } |
184 file = (ngx_cached_open_file_t *) node; | 190 |
185 | 191 if ((file->event && file->use_event) |
186 rc = ngx_strcmp(name->data, file->name); | 192 || (file->event == NULL && now - file->created < of->valid)) |
187 | 193 { |
188 if (rc == 0) { | 194 if (file->err == 0) { |
189 | 195 |
190 file->uses++; | 196 of->fd = file->fd; |
191 | 197 of->uniq = file->uniq; |
192 ngx_queue_remove(&file->queue); | 198 of->mtime = file->mtime; |
193 | 199 of->size = file->size; |
194 if (file->fd == NGX_INVALID_FILE | 200 |
195 && file->err == 0 | 201 of->is_dir = file->is_dir; |
196 && !file->is_dir) | 202 of->is_file = file->is_file; |
197 { | 203 of->is_link = file->is_link; |
198 /* file was not used often enough to be open */ | 204 of->is_exec = file->is_exec; |
199 | 205 |
200 rc = ngx_open_and_stat_file(name->data, of, pool->log); | 206 if (!file->is_dir) { |
201 | 207 file->count++; |
202 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { | 208 ngx_open_file_add_event(cache, file, of, pool->log); |
203 goto failed; | |
204 } | |
205 | |
206 goto update; | |
207 } | 209 } |
208 | 210 |
209 if (file->event || now - file->created < of->valid) { | 211 } else { |
210 | 212 of->err = file->err; |
211 if (file->err == 0) { | 213 } |
212 | 214 |
213 of->fd = file->fd; | 215 goto found; |
214 of->uniq = file->uniq; | 216 } |
215 of->mtime = file->mtime; | 217 |
216 of->size = file->size; | 218 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, |
217 | 219 "retest open file: %s, fd:%d, c:%d, e:%d", |
218 of->is_dir = file->is_dir; | 220 file->name, file->fd, file->count, file->err); |
219 of->is_file = file->is_file; | 221 |
220 of->is_link = file->is_link; | 222 if (file->is_dir) { |
221 of->is_exec = file->is_exec; | 223 |
222 | 224 /* |
223 if (!file->is_dir) { | 225 * chances that directory became file are very small |
224 file->count++; | 226 * so test_dir flag allows to use a single syscall |
225 } | 227 * in ngx_file_info() instead of three syscalls |
226 | 228 */ |
227 } else { | 229 |
228 of->err = file->err; | 230 of->test_dir = 1; |
229 } | 231 } |
230 | 232 |
231 goto found; | 233 rc = ngx_open_and_stat_file(name->data, of, pool->log); |
234 | |
235 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { | |
236 goto failed; | |
237 } | |
238 | |
239 if (of->is_dir) { | |
240 | |
241 if (file->is_dir || file->err) { | |
242 goto update; | |
243 } | |
244 | |
245 /* file became directory */ | |
246 | |
247 } else if (of->err == 0) { /* file */ | |
248 | |
249 if (file->is_dir || file->err) { | |
250 goto add_event; | |
251 } | |
252 | |
253 if (of->uniq == file->uniq | |
254 && of->mtime == file->mtime | |
255 && of->size == file->size) | |
256 { | |
257 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { | |
258 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
259 ngx_close_file_n " \"%s\" failed", | |
260 name->data); | |
232 } | 261 } |
233 | 262 |
234 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, | 263 of->fd = file->fd; |
235 "retest open file: %s, fd:%d, c:%d, e:%d", | 264 file->count++; |
236 file->name, file->fd, file->count, file->err); | 265 |
237 | 266 if (file->event) { |
238 if (file->is_dir) { | 267 file->use_event = 1; |
239 | 268 goto renew; |
240 /* | |
241 * chances that directory became file are very small | |
242 * so test_dir flag allows to use a single ngx_file_info() | |
243 * syscall instead of three syscalls | |
244 */ | |
245 | |
246 of->test_dir = 1; | |
247 } | 269 } |
248 | 270 |
249 rc = ngx_open_and_stat_file(name->data, of, pool->log); | 271 ngx_open_file_add_event(cache, file, of, pool->log); |
250 | 272 |
251 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { | 273 goto renew; |
252 goto failed; | |
253 } | |
254 | |
255 if (of->is_dir) { | |
256 if (file->is_dir || file->err) { | |
257 goto update; | |
258 } | |
259 | |
260 /* file became directory */ | |
261 | |
262 } else if (of->err == 0) { /* file */ | |
263 | |
264 if (file->is_dir || file->err) { | |
265 goto update; | |
266 } | |
267 | |
268 if (of->uniq == file->uniq | |
269 && of->mtime == file->mtime | |
270 && of->size == file->size) | |
271 { | |
272 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { | |
273 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
274 ngx_close_file_n " \"%s\" failed", | |
275 name->data); | |
276 } | |
277 | |
278 of->fd = file->fd; | |
279 file->count++; | |
280 | |
281 goto renew; | |
282 } | |
283 | |
284 /* file was changed */ | |
285 | |
286 } else { /* error to cache */ | |
287 | |
288 if (file->err || file->is_dir) { | |
289 goto update; | |
290 } | |
291 | |
292 /* file was removed, etc. */ | |
293 } | |
294 | |
295 if (file->count == 0) { | |
296 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { | |
297 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
298 ngx_close_file_n " \"%s\" failed", | |
299 name->data); | |
300 } | |
301 | |
302 goto update; | |
303 } | |
304 | |
305 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
306 | |
307 cache->current--; | |
308 | |
309 file->close = 1; | |
310 | |
311 goto create; | |
312 } | 274 } |
313 | 275 |
314 node = (rc < 0) ? node->left : node->right; | 276 /* file was changed */ |
315 | 277 |
316 } while (node != sentinel && hash == node->key); | 278 } else { /* error to cache */ |
317 | 279 |
318 break; | 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 | |
289 ngx_open_file_del_event(file); | |
290 | |
291 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { | |
292 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
293 ngx_close_file_n " \"%s\" failed", | |
294 name->data); | |
295 } | |
296 | |
297 goto add_event; | |
298 } | |
299 | |
300 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
301 | |
302 cache->current--; | |
303 | |
304 file->close = 1; | |
305 | |
306 goto create; | |
319 } | 307 } |
320 | 308 |
321 /* not found */ | 309 /* not found */ |
322 | 310 |
323 file = NULL; | 311 file = NULL; |
354 | 342 |
355 ngx_rbtree_insert(&cache->rbtree, &file->node); | 343 ngx_rbtree_insert(&cache->rbtree, &file->node); |
356 | 344 |
357 cache->current++; | 345 cache->current++; |
358 | 346 |
347 file->uses = 1; | |
359 file->count = 0; | 348 file->count = 0; |
360 file->uses = 1; | 349 file->use_event = 0; |
350 file->event = NULL; | |
351 | |
352 add_event: | |
353 | |
354 ngx_open_file_add_event(cache, file, of, pool->log); | |
361 | 355 |
362 update: | 356 update: |
363 | |
364 if (of->events | |
365 && (ngx_event_flags & NGX_USE_VNODE_EVENT) | |
366 && of->fd != NGX_INVALID_FILE) | |
367 { | |
368 file->event = ngx_calloc(sizeof(ngx_event_t), pool->log); | |
369 if (file->event== NULL) { | |
370 goto failed; | |
371 } | |
372 | |
373 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), pool->log); | |
374 if (fev == NULL) { | |
375 goto failed; | |
376 } | |
377 | |
378 fev->fd = of->fd; | |
379 fev->file = file; | |
380 fev->cache = cache; | |
381 | |
382 file->event->handler = ngx_open_file_cache_remove; | |
383 file->event->data = fev; | |
384 | |
385 /* | |
386 * although vnode event may be called while ngx_cycle->poll | |
387 * destruction; however, cleanup procedures are run before any | |
388 * memory freeing and events will be canceled. | |
389 */ | |
390 | |
391 file->event->log = ngx_cycle->log; | |
392 | |
393 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT) | |
394 != NGX_OK) | |
395 { | |
396 ngx_free(file->event->data); | |
397 ngx_free(file->event); | |
398 goto failed; | |
399 } | |
400 | |
401 } else { | |
402 file->event = NULL; | |
403 } | |
404 | 357 |
405 file->fd = of->fd; | 358 file->fd = of->fd; |
406 file->err = of->err; | 359 file->err = of->err; |
407 | 360 |
408 if (of->err == 0) { | 361 if (of->err == 0) { |
548 | 501 |
549 return NGX_OK; | 502 return NGX_OK; |
550 } | 503 } |
551 | 504 |
552 | 505 |
506 /* | |
507 * we ignore any possible event setting error and | |
508 * fallback to usual periodic file retests | |
509 */ | |
510 | |
511 static void | |
512 ngx_open_file_add_event(ngx_open_file_cache_t *cache, | |
513 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log) | |
514 { | |
515 ngx_open_file_cache_event_t *fev; | |
516 | |
517 if (!(ngx_event_flags & NGX_USE_VNODE_EVENT) | |
518 || !of->events | |
519 || file->event | |
520 || of->fd == NGX_INVALID_FILE | |
521 || file->uses < of->min_uses) | |
522 { | |
523 return; | |
524 } | |
525 | |
526 file->event = ngx_calloc(sizeof(ngx_event_t), log); | |
527 if (file->event== NULL) { | |
528 return; | |
529 } | |
530 | |
531 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log); | |
532 if (fev == NULL) { | |
533 ngx_free(file->event); | |
534 file->event = NULL; | |
535 return; | |
536 } | |
537 | |
538 fev->fd = of->fd; | |
539 fev->file = file; | |
540 fev->cache = cache; | |
541 | |
542 file->event->handler = ngx_open_file_cache_remove; | |
543 file->event->data = fev; | |
544 | |
545 /* | |
546 * although vnode event may be called while ngx_cycle->poll | |
547 * destruction, however, cleanup procedures are run before any | |
548 * memory freeing and events will be canceled. | |
549 */ | |
550 | |
551 file->event->log = ngx_cycle->log; | |
552 | |
553 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT) | |
554 != NGX_OK) | |
555 { | |
556 ngx_free(file->event->data); | |
557 ngx_free(file->event); | |
558 file->event = NULL; | |
559 return; | |
560 } | |
561 | |
562 /* | |
563 * we do not file->use_event here because there may be a race | |
564 * condition between opening file and adding event, so we rely | |
565 * upon event notification only after first file revalidation | |
566 */ | |
567 | |
568 return; | |
569 } | |
570 | |
571 | |
553 static void | 572 static void |
554 ngx_open_file_cleanup(void *data) | 573 ngx_open_file_cleanup(void *data) |
555 { | 574 { |
556 ngx_open_file_cache_cleanup_t *c = data; | 575 ngx_open_file_cache_cleanup_t *c = data; |
557 | 576 |
583 if (file->uses >= min_uses || file->count) { | 602 if (file->uses >= min_uses || file->count) { |
584 return; | 603 return; |
585 } | 604 } |
586 } | 605 } |
587 | 606 |
588 if (file->event) { | 607 ngx_open_file_del_event(file); |
589 (void) ngx_del_event(file->event, NGX_VNODE_EVENT, | |
590 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT); | |
591 | |
592 ngx_free(file->event->data); | |
593 ngx_free(file->event); | |
594 file->event = NULL; | |
595 } | |
596 | 608 |
597 if (file->count) { | 609 if (file->count) { |
598 return; | 610 return; |
599 } | 611 } |
600 | 612 |
612 return; | 624 return; |
613 } | 625 } |
614 | 626 |
615 ngx_free(file->name); | 627 ngx_free(file->name); |
616 ngx_free(file); | 628 ngx_free(file); |
629 } | |
630 | |
631 | |
632 static void | |
633 ngx_open_file_del_event(ngx_cached_open_file_t *file) | |
634 { | |
635 if (file->event == NULL) { | |
636 return; | |
637 } | |
638 | |
639 (void) ngx_del_event(file->event, NGX_VNODE_EVENT, | |
640 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT); | |
641 | |
642 ngx_free(file->event->data); | |
643 ngx_free(file->event); | |
644 file->event = NULL; | |
645 file->use_event = 0; | |
617 } | 646 } |
618 | 647 |
619 | 648 |
620 static void | 649 static void |
621 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, | 650 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, |
707 node->right = sentinel; | 736 node->right = sentinel; |
708 ngx_rbt_red(node); | 737 ngx_rbt_red(node); |
709 } | 738 } |
710 | 739 |
711 | 740 |
741 static ngx_cached_open_file_t * | |
742 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
743 uint32_t hash) | |
744 { | |
745 ngx_int_t rc; | |
746 ngx_rbtree_node_t *node, *sentinel; | |
747 ngx_cached_open_file_t *file; | |
748 | |
749 node = cache->rbtree.root; | |
750 sentinel = cache->rbtree.sentinel; | |
751 | |
752 while (node != sentinel) { | |
753 | |
754 if (hash < node->key) { | |
755 node = node->left; | |
756 continue; | |
757 } | |
758 | |
759 if (hash > node->key) { | |
760 node = node->right; | |
761 continue; | |
762 } | |
763 | |
764 /* hash == node->key */ | |
765 | |
766 do { | |
767 file = (ngx_cached_open_file_t *) node; | |
768 | |
769 rc = ngx_strcmp(name->data, file->name); | |
770 | |
771 if (rc == 0) { | |
772 return file; | |
773 } | |
774 | |
775 node = (rc < 0) ? node->left : node->right; | |
776 | |
777 } while (node != sentinel && hash == node->key); | |
778 | |
779 break; | |
780 } | |
781 | |
782 return NULL; | |
783 } | |
784 | |
785 | |
712 static void | 786 static void |
713 ngx_open_file_cache_remove(ngx_event_t *ev) | 787 ngx_open_file_cache_remove(ngx_event_t *ev) |
714 { | 788 { |
715 ngx_cached_open_file_t *file; | 789 ngx_cached_open_file_t *file; |
716 ngx_open_file_cache_event_t *fev; | 790 ngx_open_file_cache_event_t *fev; |