nghttpd: Defered eviction of cached fd using timer
To make use cache fd more robust manner (e.g. among several connections), eviction of cached file descriptor now takes place using timer. The timer is started when there is no handler (no connections). The timeout value is hard-coded and 2 seconds.
This commit is contained in:
parent
8c94341c18
commit
8b4f6f1778
|
@ -175,6 +175,14 @@ namespace {
|
||||||
void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
|
void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr ev_tstamp RELEASE_FD_TIMEOUT = 2.;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class Sessions {
|
class Sessions {
|
||||||
public:
|
public:
|
||||||
Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
|
Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
|
||||||
|
@ -185,15 +193,24 @@ public:
|
||||||
nghttp2_session_callbacks_new(&callbacks_);
|
nghttp2_session_callbacks_new(&callbacks_);
|
||||||
|
|
||||||
fill_callback(callbacks_, config_);
|
fill_callback(callbacks_, config_);
|
||||||
|
|
||||||
|
ev_timer_init(&release_fd_timer_, release_fd_cb, 0., RELEASE_FD_TIMEOUT);
|
||||||
|
release_fd_timer_.data = this;
|
||||||
}
|
}
|
||||||
~Sessions() {
|
~Sessions() {
|
||||||
|
ev_timer_stop(loop_, &release_fd_timer_);
|
||||||
for (auto handler : handlers_) {
|
for (auto handler : handlers_) {
|
||||||
delete handler;
|
delete handler;
|
||||||
}
|
}
|
||||||
nghttp2_session_callbacks_del(callbacks_);
|
nghttp2_session_callbacks_del(callbacks_);
|
||||||
}
|
}
|
||||||
void add_handler(Http2Handler *handler) { handlers_.insert(handler); }
|
void add_handler(Http2Handler *handler) { handlers_.insert(handler); }
|
||||||
void remove_handler(Http2Handler *handler) { handlers_.erase(handler); }
|
void remove_handler(Http2Handler *handler) {
|
||||||
|
handlers_.erase(handler);
|
||||||
|
if (handlers_.empty() && !fd_cache_.empty()) {
|
||||||
|
ev_timer_again(loop_, &release_fd_timer_);
|
||||||
|
}
|
||||||
|
}
|
||||||
SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; }
|
SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; }
|
||||||
SSL *ssl_session_new(int fd) {
|
SSL *ssl_session_new(int fd) {
|
||||||
SSL *ssl = SSL_new(ssl_ctx_);
|
SSL *ssl = SSL_new(ssl_ctx_);
|
||||||
|
@ -275,12 +292,33 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto &ent = (*i).second;
|
auto &ent = (*i).second;
|
||||||
if (--ent.usecount == 0) {
|
if (ent.usecount == 0) {
|
||||||
|
// temporary fd, close it immediately
|
||||||
close(ent.fd);
|
close(ent.fd);
|
||||||
fd_cache_.erase(i);
|
fd_cache_.erase(i);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
--ent.usecount;
|
||||||
|
|
||||||
|
// We use timer to close file descriptor and delete the entry from
|
||||||
|
// cache. The timer will be started when there is no handler.
|
||||||
|
}
|
||||||
|
void release_unused_fd() {
|
||||||
|
for (auto i = std::begin(fd_cache_); i != std::end(fd_cache_);) {
|
||||||
|
auto &ent = (*i).second;
|
||||||
|
if (ent.usecount != 0) {
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(ent.fd);
|
||||||
|
i = fd_cache_.erase(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const HttpServer *get_server() const { return sv_; }
|
const HttpServer *get_server() const { return sv_; }
|
||||||
|
bool handlers_empty() const { return handlers_.empty(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<Http2Handler *> handlers_;
|
std::set<Http2Handler *> handlers_;
|
||||||
|
@ -291,11 +329,26 @@ private:
|
||||||
const Config *config_;
|
const Config *config_;
|
||||||
SSL_CTX *ssl_ctx_;
|
SSL_CTX *ssl_ctx_;
|
||||||
nghttp2_session_callbacks *callbacks_;
|
nghttp2_session_callbacks *callbacks_;
|
||||||
|
ev_timer release_fd_timer_;
|
||||||
int64_t next_session_id_;
|
int64_t next_session_id_;
|
||||||
ev_tstamp tstamp_cached_;
|
ev_tstamp tstamp_cached_;
|
||||||
std::string cached_date_;
|
std::string cached_date_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
|
auto sessions = static_cast<Sessions *>(w->data);
|
||||||
|
|
||||||
|
ev_timer_stop(loop, w);
|
||||||
|
|
||||||
|
if (!sessions->handlers_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sessions->release_unused_fd();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||||
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
||||||
stream_id(stream_id), echo_upload(false) {
|
stream_id(stream_id), echo_upload(false) {
|
||||||
|
@ -979,8 +1032,9 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
|
||||||
unlink(tempfn);
|
unlink(tempfn);
|
||||||
// Ordinary request never start with "echo:". The length is 0 for
|
// Ordinary request never start with "echo:". The length is 0 for
|
||||||
// now. We will update it when we get whole request body.
|
// now. We will update it when we get whole request body.
|
||||||
stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn,
|
auto path = std::string("echo:") + tempfn;
|
||||||
FileEntry(tempfn, 0, 0, fd, nullptr));
|
stream->file_ent =
|
||||||
|
sessions->cache_fd(path, FileEntry(path, 0, 0, fd, nullptr, 0));
|
||||||
stream->echo_upload = true;
|
stream->echo_upload = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,14 +83,15 @@ struct Config {
|
||||||
class Http2Handler;
|
class Http2Handler;
|
||||||
|
|
||||||
struct FileEntry {
|
struct FileEntry {
|
||||||
|
// To close fd immediately when nothing refers to this entry, give 0
|
||||||
|
// to |usecount|.
|
||||||
FileEntry(std::string path, int64_t length, int64_t mtime, int fd,
|
FileEntry(std::string path, int64_t length, int64_t mtime, int fd,
|
||||||
const std::string *content_type)
|
const std::string *content_type, int usecount = 1)
|
||||||
: path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr),
|
: path(std::move(path)), length(length), mtime(mtime),
|
||||||
dlnext(nullptr), content_type(content_type), fd(fd), usecount(1) {}
|
content_type(content_type), fd(fd), usecount(usecount) {}
|
||||||
std::string path;
|
std::string path;
|
||||||
int64_t length;
|
int64_t length;
|
||||||
int64_t mtime;
|
int64_t mtime;
|
||||||
FileEntry *dlprev, *dlnext;
|
|
||||||
const std::string *content_type;
|
const std::string *content_type;
|
||||||
int fd;
|
int fd;
|
||||||
int usecount;
|
int usecount;
|
||||||
|
|
Loading…
Reference in New Issue