diff options
Diffstat (limited to 'source/cache.c')
| -rw-r--r-- | source/cache.c | 37 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 file changed, 34 insertions, 3 deletions
diff --git a/source/cache.c b/source/cache.c index 435cc96..95275fb 100644 --- a/source/cache.c +++ b/source/cache.c @@ -55,6 +55,10 @@ struct cache_slot { const char *path; const char *lock_path; int key_matches; + // Set when the fill was abandoned part way through, meaning the error + // page has already reached the visitor and nothing more may be served + // after it, not the lock file and not the stale copy still open. + int abandoned; // The slot as it was when it was opened, or the lock file once // fill_slot has written a page into it. struct stat st; @@ -272,11 +276,14 @@ void cache_abandon_fill(void) if (!slot) return; slot_being_filled = NULL; + slot->abandoned = 1; - // Emptied while stdout still points at the lock file, so the half + // The page is sitting in html.c's buffer and in stdio's, and both are + // emptied while stdout still points at the lock file, so the half // rendered page goes into the file about to be removed rather than // reaching the client ahead of whatever is written next. html_flush(); + fflush(stdout); if (slot->saved_stdout >= 0) { dup2(slot->saved_stdout, STDOUT_FILENO); @@ -305,11 +312,15 @@ static int fill_slot(struct cache_slot *slot) // The page is sitting in html.c's buffer and then in stdio's, and all // of it has to reach the lock file before that file is renamed into - // place. + // place. After an abandoned fill stdout is the client again and this + // same flush delivers the tail of the error page instead. html_flush(); if (fflush(stdout)) return errno; + if (slot->abandoned) + return 0; + // print_slot takes the length of what it copies from here, and what // it copies after a fill is the lock file rather than the old slot. if (fstat(slot->lock_fd, &slot->st)) @@ -334,6 +345,10 @@ static void refresh_slot(struct cache_slot *slot) if (is_modified(slot) || fill_slot(slot)) { unlock_slot(slot, 0); close_lock(slot); + } else if (slot->abandoned) { + // The abandoned fill answered the visitor itself and removed + // the lock file, so only the descriptor is left to clean up. + close_lock(slot); } else { close_slot(slot); unlock_slot(slot, 1); @@ -349,6 +364,13 @@ static int process_slot(struct cache_slot *slot) if (!err && slot->key_matches) { if (is_expired(slot)) refresh_slot(slot); + // A refresh the error page abandoned has already answered the + // visitor, and serving the stale copy still open would append + // a second page to that answer. + if (slot->abandoned) { + close_slot(slot); + return 0; + } err = serve_slot(slot); close_slot(slot); return err; @@ -378,7 +400,15 @@ static int process_slot(struct cache_slot *slot) slot->lock_path, strerror(err), err); unlock_slot(slot, 0); close_lock(slot); - slot->fn(); + // Rendering again is only right when nothing was delivered, + // and an abandoned fill has already sent the error page. + if (!slot->abandoned) + slot->fn(); + return 0; + } + + if (slot->abandoned) { + close_lock(slot); return 0; } @@ -466,6 +496,7 @@ int cache_process(int size, const char *path, const char *key, int ttl, slot.fn = fn; slot.ttl = ttl; slot.saved_stdout = -1; + slot.abandoned = 0; slot.path = slot_path.buf; slot.lock_path = lock_path.buf; slot.key = key; |
