From ddb0448a96a754957301bfdaf4393811addf9d1e Mon Sep 17 00:00:00 2001 From: Thomas Porzelt Date: Thu, 26 Mar 2009 23:05:17 +0100 Subject: [PATCH] rework stat_cache --- include/lighttpd/stat_cache.h | 36 +++-- include/lighttpd/virtualrequest.h | 2 +- src/modules/mod_dirlist.c | 33 +++-- src/plugin_core.c | 74 ++++------ src/stat_cache.c | 227 ++++++++++++++++-------------- src/virtualrequest.c | 19 ++- 6 files changed, 201 insertions(+), 190 deletions(-) diff --git a/include/lighttpd/stat_cache.h b/include/lighttpd/stat_cache.h index 85920c3..731af38 100644 --- a/include/lighttpd/stat_cache.h +++ b/include/lighttpd/stat_cache.h @@ -1,16 +1,15 @@ /* * stat cache - speeding up stat()s * - * The basic idea behind the stat cache is to reduce calls to stat() which might be slow due to disk io (some ms). + * The basic idea behind the stat cache is to reduce calls to stat() which might block due to disk io (some ms). * Each worker thread has its own cache so no locking contention between threads happens which could be slow. - * This means that there will be more stat() calls than there would be with only one shared cache but since there + * This means that there will be more blocking stat() calls than there would be with only one shared cache but since there * should be mostly hits in most cases (few items requested frequently) it will outweight the locking contention. * To prevent the stat() from blocking all other requests of that worker, we hand it over to another thread. * * Entries are removed after 10 seconds (adjustable through stat_cache.ttl setup) * * TODO: - * - stat_cache.ttl setup * - create ETAGs * - get content type from xattr * - add support for inotify (linux). TTL for entries can be increased to 60s @@ -19,13 +18,14 @@ * If a stat is requested, the following procedure takes place: * - a cache lookup is performed * - in case of a cache HIT: - * - if state is FINISHED and entry is fresh then return entry - * - if state is FINISHED but entry old then reset entry, create new job and return NULL - * - if state is WAITING then add vrequest to entry and return NULL (looks like a cache miss) + * - if state is FINISHED and entry is fresh then return HANDLER_GO_ON + * - if state is WAITING then add vrequest to entry and return HANDLER_WAIT_FOR_EVENT (looks like a cache miss) * - in case of a cache MISS: * - a new entry is allocated and inserted into the cache, state is set to WAITING * - the entry is inserted into the delete queue - * - a new job is created and NULL returned + * - a new job is created and HANDLER_WAIT_FOR_EVENT returned + * - in case of an ERROR: + * - return HANDLER_ERROR * * In the delete queue callback we check if no vrequests are working on that entry. If yes, we free it. If not then we requeue it. * Locking only happens in two cases: 1) a new job is send to the stat thread 2) the stat thread sends the info back to the worker. @@ -60,15 +60,16 @@ struct stat_cache_entry { } state; stat_cache_entry_data data; - GArray *dirlist; /* array of stat_cache_dirlist_entry, used together with STAT_CACHE_ENTRY_DIR */ - ev_tstamp ts; /* timestamp the entry was created (not when the stat() was done) */ + GArray *dirlist; /* array of stat_cache_entry_data, used together with STAT_CACHE_ENTRY_DIR */ + GPtrArray *vrequests; /* vrequests waiting for this info */ guint refcount; waitqueue_elem queue_elem; /* queue element for the delete_queue */ - gboolean in_cache; + gboolean cached; }; struct stat_cache { + GHashTable *dirlists; GHashTable *entries; GAsyncQueue *job_queue_out; /* elements waiting for stat */ GAsyncQueue *job_queue_in; /* elements with finished stat */ @@ -87,14 +88,19 @@ void stat_cache_free(stat_cache *sc); /* gets a stat_cache_entry for a specified path - returns NULL in case of a cache MISS and you should return HANDLER_WAIT_FOR_EVENT + if fd is set, a new fd is acquired via open() and stat info via fstat(), otherwise only a stat() is performed + returns HANDLER_WAIT_FOR_EVENT in case of a cache MISS, HANDLER_GO_ON in case of a hit and HANDLER_ERROR in case of an error */ -LI_API stat_cache_entry *stat_cache_get(vrequest *vr, GString *path); +LI_API handler_t stat_cache_get(vrequest *vr, GString *path, struct stat *st, int *err, int *fd); -/* like stat_cache_entry_get but gets stat info for a whole directory */ -LI_API stat_cache_entry *stat_cache_get_dir(vrequest *vr, GString *path); +/* + sce->dirlist will contain a list of stat_cache_entry_data upon success + returns HANDLER_WAIT_FOR_EVENT in case of a cache MISS, HANDLER_GO_ON in case of a hit and HANDLER_ERROR in case of an error +*/ +LI_API handler_t stat_cache_get_dirlist(vrequest *vr, GString *path, stat_cache_entry **result); +LI_API void stat_cache_entry_acquire(vrequest *vr, stat_cache_entry *sce); /* release a stat_cache_entry so it can be cleaned up */ -LI_API void stat_cache_entry_release(vrequest *vr); +LI_API void stat_cache_entry_release(vrequest *vr, stat_cache_entry *sce); #endif \ No newline at end of file diff --git a/include/lighttpd/virtualrequest.h b/include/lighttpd/virtualrequest.h index 7a81f48..57225c1 100644 --- a/include/lighttpd/virtualrequest.h +++ b/include/lighttpd/virtualrequest.h @@ -84,7 +84,7 @@ struct vrequest { GList *job_queue_link; - stat_cache_entry *stat_cache_entry; + GPtrArray *stat_cache_entries; }; #define VREQUEST_WAIT_FOR_RESPONSE_HEADERS(vr) \ diff --git a/src/modules/mod_dirlist.c b/src/modules/mod_dirlist.c index c33b245..77e5a18 100644 --- a/src/modules/mod_dirlist.c +++ b/src/modules/mod_dirlist.c @@ -30,7 +30,7 @@ * dirlist ("include-header" => true, "hide-header" => true, "hide->suffix" => (".bak")); * } * - shows a directory listing including the content of HEADER.txt above the list and hiding itself from it - * also hides all files all files ending in ".bak" + * also hides all files ending in ".bak" * * Tip: * xyz @@ -166,45 +166,44 @@ static handler_t dirlist(vrequest *vr, gpointer param, gpointer *context) { stat_cache_entry *sce; dirlist_data *dd; dirlist_plugin_data *pd; + UNUSED(context); - + if (vrequest_is_handled(vr)) return HANDLER_GO_ON; - if (!vr->stat_cache_entry) { - if (vr->physical.path->len == 0) return HANDLER_GO_ON; + if (vr->physical.path->len == 0) return HANDLER_GO_ON; + + switch (stat_cache_get_dirlist(vr, vr->physical.path, &sce)) { + case HANDLER_GO_ON: break; + case HANDLER_WAIT_FOR_EVENT: return HANDLER_WAIT_FOR_EVENT; + default: return HANDLER_ERROR; } dd = param; pd = dd->plugin->data; - sce = stat_cache_get_dir(vr, vr->physical.path); - if (!sce) - return HANDLER_WAIT_FOR_EVENT; - if (sce->data.failed) { /* stat failed */ + stat_cache_entry_release(vr, sce);VR_ERROR(vr, "stat('%s') failed: %s", sce->data.path->str, g_strerror(sce->data.err)); switch (sce->data.err) { case ENOENT: case ENOTDIR: - stat_cache_entry_release(vr); return HANDLER_GO_ON; case EACCES: if (!vrequest_handle_direct(vr)) return HANDLER_ERROR; vr->response.http_status = 403; - stat_cache_entry_release(vr); return HANDLER_GO_ON; default: VR_ERROR(vr, "stat('%s') failed: %s", sce->data.path->str, g_strerror(sce->data.err)); - stat_cache_entry_release(vr); return HANDLER_ERROR; } } else if (!S_ISDIR(sce->data.st.st_mode)) { - stat_cache_entry_release(vr); + stat_cache_entry_release(vr, sce);VR_DEBUG(vr, "%s", "no dir"); return HANDLER_GO_ON; } else if (vr->request.uri.path->str[vr->request.uri.path->len-1] != G_DIR_SEPARATOR) { GString *host, *uri; if (!vrequest_handle_direct(vr)) { - stat_cache_entry_release(vr); + stat_cache_entry_release(vr, sce); return HANDLER_ERROR; } /* redirect to scheme + host + path + / + querystring if directory without trailing slash */ @@ -230,7 +229,7 @@ static handler_t dirlist(vrequest *vr, gpointer param, gpointer *context) { vr->response.http_status = 301; http_header_overwrite(vr->response.headers, CONST_STR_LEN("Location"), GSTR_LEN(uri)); g_string_free(uri, TRUE); - stat_cache_entry_release(vr); + stat_cache_entry_release(vr, sce); return HANDLER_GO_ON; } else { /* everything ok, we have the directory listing */ @@ -246,7 +245,7 @@ static handler_t dirlist(vrequest *vr, gpointer param, gpointer *context) { gboolean hide; if (!vrequest_handle_direct(vr)) { - stat_cache_entry_release(vr); + stat_cache_entry_release(vr, sce); return HANDLER_ERROR; } vr->response.http_status = 200; @@ -258,7 +257,7 @@ static handler_t dirlist(vrequest *vr, gpointer param, gpointer *context) { etag_set_header(vr, &sce->data.st, &cachable); if (cachable) { vr->response.http_status = 304; - stat_cache_entry_release(vr); + stat_cache_entry_release(vr, sce); return HANDLER_GO_ON; } @@ -408,7 +407,7 @@ static handler_t dirlist(vrequest *vr, gpointer param, gpointer *context) { g_array_free(files, TRUE); } - stat_cache_entry_release(vr); + stat_cache_entry_release(vr, sce); return HANDLER_GO_ON; } diff --git a/src/plugin_core.c b/src/plugin_core.c index bda33a8..504fbad 100644 --- a/src/plugin_core.c +++ b/src/plugin_core.c @@ -186,7 +186,9 @@ static action* core_docroot(server *srv, plugin* p, value *val) { static handler_t core_handle_static(vrequest *vr, gpointer param, gpointer *context) { int fd; - stat_cache_entry *sce; + struct stat st; + int err; + handler_t res; UNUSED(param); UNUSED(context); @@ -201,63 +203,45 @@ static handler_t core_handle_static(vrequest *vr, gpointer param, gpointer *cont if (vrequest_is_handled(vr)) return HANDLER_GO_ON; - if (!vr->stat_cache_entry) { - if (vr->physical.path->len == 0) return HANDLER_GO_ON; - } + if (vr->physical.path->len == 0) return HANDLER_GO_ON; - sce = stat_cache_get(vr, vr->physical.path); - if (!sce) - return HANDLER_WAIT_FOR_EVENT; + res = stat_cache_get(vr, vr->physical.path, &st, &err, &fd); + if (res == HANDLER_WAIT_FOR_EVENT) + return res; if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "try serving static file: '%s'", vr->physical.path->str); } - if (sce->data.failed) { - /* stat failed */ - stat_cache_entry_release(vr); + if (res == HANDLER_ERROR) { + /* open or fstat failed */ - switch (sce->data.err) { - case ENOENT: - case ENOTDIR: - return HANDLER_GO_ON; - case EACCES: - if (!vrequest_handle_direct(vr)) { - return HANDLER_ERROR; - } - vr->response.http_status = 403; - VR_DEBUG(vr, "stat('%s') failed: %s", sce->data.path->str, g_strerror(sce->data.err)); - return HANDLER_GO_ON; - default: - VR_ERROR(vr, "stat('%s') failed: %s", sce->data.path->str, g_strerror(sce->data.err)); + if (!vrequest_handle_direct(vr)) { return HANDLER_ERROR; } - } else if (S_ISDIR(sce->data.st.st_mode)) { - stat_cache_entry_release(vr); + + switch (err) { + case ENOENT: + case ENOTDIR: + vr->response.http_status = 404; + return HANDLER_GO_ON; + case EACCES: + vr->response.http_status = 403; + return HANDLER_GO_ON; + default: + VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err)); + return HANDLER_ERROR; + } + } else if (S_ISDIR(st.st_mode)) { return HANDLER_GO_ON; - } else if (!S_ISREG(sce->data.st.st_mode)) { + } else if (!S_ISREG(st.st_mode)) { if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "not a regular file: '%s'", vr->physical.path->str); } if (!vrequest_handle_direct(vr)) { - stat_cache_entry_release(vr); return HANDLER_ERROR; } vr->response.http_status = 403; - } else if ((fd = open(vr->physical.path->str, O_RDONLY)) == -1) { - stat_cache_entry_release(vr); - switch (errno) { - case ENOENT: - case ENOTDIR: - return HANDLER_GO_ON; - case EACCES: - if (!vrequest_handle_direct(vr)) return HANDLER_ERROR; - vr->response.http_status = 403; - return HANDLER_GO_ON; - default: - VR_DEBUG(vr, "open('%s') failed: %s", vr->physical.path->str, g_strerror(errno)); - return HANDLER_ERROR; - } } else { GString *mime_str; gboolean cachable; @@ -267,15 +251,13 @@ static handler_t core_handle_static(vrequest *vr, gpointer param, gpointer *cont if (!vrequest_handle_direct(vr)) { close(fd); - stat_cache_entry_release(vr); return HANDLER_ERROR; } - etag_set_header(vr, &sce->data.st, &cachable); + etag_set_header(vr, &st, &cachable); if (cachable) { vr->response.http_status = 304; close(fd); - stat_cache_entry_release(vr); return HANDLER_GO_ON; } @@ -285,11 +267,9 @@ static handler_t core_handle_static(vrequest *vr, gpointer param, gpointer *cont http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str)); else http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); - chunkqueue_append_file_fd(vr->out, NULL, 0, sce->data.st.st_size, fd); + chunkqueue_append_file_fd(vr->out, NULL, 0, st.st_size, fd); } - stat_cache_entry_release(vr); - return HANDLER_GO_ON; } diff --git a/src/stat_cache.c b/src/stat_cache.c index d48c1e4..1d912b5 100644 --- a/src/stat_cache.c +++ b/src/stat_cache.c @@ -1,11 +1,12 @@ #include +#include +#include static void stat_cache_job_cb(struct ev_loop *loop, ev_async *w, int revents); static void stat_cache_delete_cb(struct ev_loop *loop, ev_timer *w, int revents); static gpointer stat_cache_thread(gpointer data); static void stat_cache_entry_free(stat_cache_entry *sce); -static stat_cache_entry *stat_cache_get_internal(vrequest *vr, GString *path, gboolean dir); void stat_cache_new(worker *wrk, gdouble ttl) { stat_cache *sc; @@ -18,6 +19,7 @@ void stat_cache_new(worker *wrk, gdouble ttl) { sc = g_slice_new0(stat_cache); sc->ttl = ttl; sc->entries = g_hash_table_new_full((GHashFunc)g_string_hash, (GEqualFunc)g_string_equal, NULL, NULL); + sc->dirlists = g_hash_table_new_full((GHashFunc)g_string_hash, (GEqualFunc)g_string_equal, NULL, NULL); sc->job_queue_in = g_async_queue_new(); sc->job_queue_out = g_async_queue_new(); @@ -61,6 +63,7 @@ void stat_cache_free(stat_cache *sc) { g_async_queue_unref(sc->job_queue_in); g_async_queue_unref(sc->job_queue_out); g_hash_table_destroy(sc->entries); + g_hash_table_destroy(sc->dirlists); g_slice_free(stat_cache, sc); } @@ -75,13 +78,21 @@ static void stat_cache_delete_cb(struct ev_loop *loop, ev_timer *w, int revents) while ((wqe = waitqueue_pop(&sc->delete_queue)) != NULL) { /* stat cache entry TTL over */ sce = wqe->data; + + if (sce->cached) { + if (sce->type == STAT_CACHE_ENTRY_SINGLE) + g_hash_table_remove(sc->entries, sce->data.path); + else + g_hash_table_remove(sc->dirlists, sce->data.path); + + sce->cached = FALSE; + } + if (sce->refcount) { /* if there are still vrequests using this entry just requeue it */ waitqueue_push(&sc->delete_queue, wqe); } else { /* no more vrequests using this entry, finally free it */ - if (sce->in_cache) - g_hash_table_remove(sc->entries, sce->data.path); stat_cache_entry_free(sce); } } @@ -89,6 +100,7 @@ static void stat_cache_delete_cb(struct ev_loop *loop, ev_timer *w, int revents) waitqueue_update(&sc->delete_queue); } +/* called whenever an async stat job has finished */ static void stat_cache_job_cb(struct ev_loop *loop, ev_async *w, int revents) { guint i; stat_cache_entry *sce; @@ -102,12 +114,18 @@ static void stat_cache_job_cb(struct ev_loop *loop, ev_async *w, int revents) { if (sce->data.failed) sc->errors++; + /* queue pending vrequests */ for (i = 0; i < sce->vrequests->len; i++) { vr = g_ptr_array_index(sce->vrequests, i); vrequest_joblist_append(vr); + if (sce->type == STAT_CACHE_ENTRY_SINGLE) { + sce->refcount--; + g_ptr_array_remove_fast(vr->stat_cache_entries, sce); + } } g_ptr_array_set_size(sce->vrequests, 0); + sce->refcount--; } } @@ -183,7 +201,7 @@ static gpointer stat_cache_thread(gpointer data) { g_string_truncate(str, sce->data.path->len); /* make sure the path ends with / (or whatever) */ - if (sce->data.path->str[sce->data.path->len-1] != G_DIR_SEPARATOR) + if (!sce->data.path->len || sce->data.path->str[sce->data.path->len-1] != G_DIR_SEPARATOR) g_string_append_c(str, G_DIR_SEPARATOR); g_string_append_len(str, GSTR_LEN(sced.path)); @@ -216,126 +234,121 @@ static gpointer stat_cache_thread(gpointer data) { return NULL; } -static stat_cache_entry *stat_cache_get_internal(vrequest *vr, GString *path, gboolean dir) { - stat_cache *sc; +static stat_cache_entry *stat_cache_entry_new(GString *path) { stat_cache_entry *sce; - sc = vr->con->wrk->stat_cache; + sce = g_slice_new0(stat_cache_entry); + sce->data.path = g_string_new_len(GSTR_LEN(path)); + sce->vrequests = g_ptr_array_sized_new(8); + sce->state = STAT_CACHE_ENTRY_WAITING; + sce->queue_elem.data = sce; + sce->refcount = 1; + sce->cached = TRUE; - /* lookup entry in cache */ + return sce; +} + +handler_t stat_cache_get_dirlist(vrequest *vr, GString *path, stat_cache_entry **result) { + stat_cache *sc; + stat_cache_entry *sce; + guint i; + + sc = vr->con->wrk->stat_cache; + sce = g_hash_table_lookup(sc->dirlists, path); + + if (sce) { + /* cache hit, check state */ + if (g_atomic_int_get(&sce->state) == STAT_CACHE_ENTRY_WAITING) { + /* already waiting for it? */ + for (i = 0; i < vr->stat_cache_entries->len; i++) { + if (g_ptr_array_index(vr->stat_cache_entries, i) == sce) + return HANDLER_WAIT_FOR_EVENT; + } + stat_cache_entry_acquire(vr, sce); + return HANDLER_WAIT_FOR_EVENT; + } + + sce->refcount++; + g_ptr_array_add(vr->stat_cache_entries, sce); + sc->hits++; + *result = sce; + return HANDLER_GO_ON; + } else { + /* cache miss, allocate new entry */ + sce = stat_cache_entry_new(path); + sce->type = STAT_CACHE_ENTRY_DIR; + sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(stat_cache_entry_data), 32); + stat_cache_entry_acquire(vr, sce); + waitqueue_push(&sc->delete_queue, &sce->queue_elem); + g_hash_table_insert(sc->dirlists, sce->data.path, sce); + g_async_queue_push(sc->job_queue_out, sce); + sc->misses++; + return HANDLER_WAIT_FOR_EVENT; + } +} + +handler_t stat_cache_get(vrequest *vr, GString *path, struct stat *st, int *err, int *fd) { + stat_cache *sc; + stat_cache_entry *sce; + guint i; + + sc = vr->con->wrk->stat_cache; sce = g_hash_table_lookup(sc->entries, path); if (sce) { /* cache hit, check state */ - if (g_atomic_int_get(&sce->state) == STAT_CACHE_ENTRY_FINISHED) { - /* stat info available, check if it is fresh */ - if (!((sce->type == STAT_CACHE_ENTRY_DIR) ^ dir) && sce->ts >= (CUR_TS(vr->con->wrk) - (ev_tstamp)sc->ttl)) { - /* entry fresh */ - if (!vr->stat_cache_entry) { - sc->hits++; - vr->stat_cache_entry = sce; - sce->refcount++; - } - - return sce; - } else { - /* entry old */ - if (sce->refcount == 0) { - /* no vrequests working on the entry, reuse it */ - if (sce->type == STAT_CACHE_ENTRY_DIR) { - guint i; - - /* free old entries */ - for (i = 0; i < sce->dirlist->len; i++) { - g_string_free(g_array_index(sce->dirlist, stat_cache_entry_data, i).path, TRUE); - } - - if (!dir) { - g_array_free(sce->dirlist, TRUE); - sce->type = STAT_CACHE_ENTRY_SINGLE; - } else { - g_array_set_size(sce->dirlist, 0); - } - } else { - /* single file */ - if (dir) { - sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(stat_cache_entry_data), 32); - sce->type = STAT_CACHE_ENTRY_DIR; - } - } - } else { - /* there are still vrequests using this entry, replace with a new one */ - sce->in_cache = FALSE; - sce = g_slice_new0(stat_cache_entry); - sce->data.path = g_string_new_len(GSTR_LEN(path)); - sce->vrequests = g_ptr_array_sized_new(8); - sce->in_cache = TRUE; - sce->queue_elem.data = sce; - g_hash_table_replace(sc->entries, sce->data.path, sce); - if (dir) { - sce->type = STAT_CACHE_ENTRY_DIR; - sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(stat_cache_entry_data), 32); - } else { - sce->type = STAT_CACHE_ENTRY_SINGLE; - } - } - - sce->ts = CUR_TS(vr->con->wrk); - vr->stat_cache_entry = sce; - g_ptr_array_add(sce->vrequests, vr); - sce->refcount++; - waitqueue_push(&sc->delete_queue, &sce->queue_elem); - sce->state = STAT_CACHE_ENTRY_WAITING; - g_async_queue_push(sc->job_queue_out, sce); - sc->misses++; - return NULL; + if (g_atomic_int_get(&sce->state) == STAT_CACHE_ENTRY_WAITING) { + /* already waiting for it? */ + for (i = 0; i < vr->stat_cache_entries->len; i++) { + if (g_ptr_array_index(vr->stat_cache_entries, i) == sce) + return HANDLER_WAIT_FOR_EVENT; } - } else { - /* stat info not available (state is STAT_CACHE_ENTRY_WAITING) */ - vr->stat_cache_entry = sce; - g_ptr_array_add(sce->vrequests, vr); - sce->refcount++; - sc->misses++; - return NULL; + stat_cache_entry_acquire(vr, sce); + return HANDLER_WAIT_FOR_EVENT; } + + sc->hits++; } else { /* cache miss, allocate new entry */ - sce = g_slice_new0(stat_cache_entry); - sce->data.path = g_string_new_len(GSTR_LEN(path)); - sce->vrequests = g_ptr_array_sized_new(8); - sce->ts = CUR_TS(vr->con->wrk); - sce->state = STAT_CACHE_ENTRY_WAITING; - sce->in_cache = TRUE; - sce->queue_elem.data = sce; - vr->stat_cache_entry = sce; - g_ptr_array_add(sce->vrequests, vr); - sce->refcount = 1; - sc->misses++; - - if (dir) { - sce->type = STAT_CACHE_ENTRY_DIR; - sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(stat_cache_entry_data), 32); - } else { - sce->type = STAT_CACHE_ENTRY_SINGLE; - } - + sce = stat_cache_entry_new(path); + sce->type = STAT_CACHE_ENTRY_SINGLE; + stat_cache_entry_acquire(vr, sce); waitqueue_push(&sc->delete_queue, &sce->queue_elem); g_hash_table_insert(sc->entries, sce->data.path, sce); g_async_queue_push(sc->job_queue_out, sce); - - return NULL; + sc->misses++; + return HANDLER_WAIT_FOR_EVENT; } + + if (fd) { + /* open + fstat */ + if (-1 == (*fd = open(path->str, O_RDONLY))) { + *err = errno; + return HANDLER_ERROR; + } + if (-1 == fstat(*fd, st)) { + *err = errno; + return HANDLER_ERROR; + } + } else { + /* stat */ + if (-1 == stat(path->str, st)) { + *err = errno; + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; } -stat_cache_entry *stat_cache_get(vrequest *vr, GString *path) { - return stat_cache_get_internal(vr, path, FALSE); +void stat_cache_entry_acquire(vrequest *vr, stat_cache_entry *sce) { + sce->refcount++; + g_ptr_array_add(vr->stat_cache_entries, sce); + g_ptr_array_add(sce->vrequests, vr); } -stat_cache_entry *stat_cache_get_dir(vrequest *vr, GString *path) { - return stat_cache_get_internal(vr, path, TRUE); -} - -void stat_cache_entry_release(vrequest *vr) { - vr->stat_cache_entry->refcount--; - vr->stat_cache_entry = NULL; +void stat_cache_entry_release(vrequest *vr, stat_cache_entry *sce) { + sce->refcount--; + g_ptr_array_remove_fast(sce->vrequests, vr); + g_ptr_array_remove_fast(vr->stat_cache_entries, sce); } diff --git a/src/virtualrequest.c b/src/virtualrequest.c index cce2575..e38c022 100644 --- a/src/virtualrequest.c +++ b/src/virtualrequest.c @@ -126,12 +126,16 @@ vrequest* vrequest_new(connection *con, vrequest_handler handle_response_headers vr->out = vr->filters_out.in; vr->vr_out = vr->filters_out.out; + vr->stat_cache_entries = g_ptr_array_sized_new(2); + action_stack_init(&vr->action_stack); return vr; } void vrequest_free(vrequest* vr) { + guint i; + action_stack_clear(vr, &vr->action_stack); plugins_handle_vrclose(vr); g_ptr_array_free(vr->plugin_ctx, TRUE); @@ -151,10 +155,19 @@ void vrequest_free(vrequest* vr) { g_slice_free1(vr->con->srv->option_def_values->len * sizeof(option_value), vr->options); + + for (i = 0; i < vr->stat_cache_entries->len; i++) { + stat_cache_entry *sce = g_ptr_array_index(vr->stat_cache_entries, i); + stat_cache_entry_release(vr, sce); + } + g_ptr_array_free(vr->stat_cache_entries, TRUE); + g_slice_free(vrequest, vr); } void vrequest_reset(vrequest *vr) { + guint i; + action_stack_reset(vr, &vr->action_stack); plugins_handle_vrclose(vr); { @@ -180,9 +193,9 @@ void vrequest_reset(vrequest *vr) { vr->job_queue_link = NULL; } - if (vr->stat_cache_entry) { - g_ptr_array_remove_fast(vr->stat_cache_entry->vrequests, vr); - stat_cache_entry_release(vr); + for (i = 0; i < vr->stat_cache_entries->len; i++) { + stat_cache_entry *sce = g_ptr_array_index(vr->stat_cache_entries, i); + stat_cache_entry_release(vr, sce); } memcpy(vr->options, vr->con->srv->option_def_values->data, vr->con->srv->option_def_values->len * sizeof(option_value));