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;