diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Serve nothing further once a fill is abandoned
Diffstat (limited to 'source/cache.c')
-rw-r--r--source/cache.c37
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;