diff --git a/src/rpc/scgi.cc b/src/rpc/scgi.cc index 4f665547..9d601675 100644 --- a/src/rpc/scgi.cc +++ b/src/rpc/scgi.cc @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include "control.h" @@ -25,6 +27,8 @@ SCgi::SCgi() { std::generate(m_tasks.begin(), m_tasks.end(), []() { return std::make_unique(); }); m_current = m_tasks.begin(); + + m_resume_accept.slot() = [this] { resume_accept(); }; } SCgi::~SCgi() { @@ -121,6 +125,9 @@ SCgi::stop() { if (!is_open()) return; + if (m_resume_accept.is_scheduled()) + torrent::this_thread::scheduler()->erase(&m_resume_accept); + for (auto& itr : m_tasks) { if (itr->is_open()) itr->close(); @@ -158,9 +165,16 @@ SCgi::event_read() { return; // Force a new event_read() call just to be sure we don't enter an infinite loop. - if (errno == ECONNABORTED) + if (errno == ECONNABORTED || errno == EPROTO || errno == EPERM) return; + // Out of file descriptors or memory: the connection stays queued and the socket stays readable, + // so stop reading it for a while instead of spinning. + if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS || errno == ENOMEM) { + pause_accept(errno); + return; + } + throw torrent::resource_error("Listener port accept() failed: " + std::string(std::strerror(errno))); } @@ -189,6 +203,22 @@ SCgi::event_read() { } } +void +SCgi::pause_accept(int error) { + lt_log_print(torrent::LOG_ERRORS, "scgi: accept() failed: %s : not accepting connections for a second", std::strerror(error)); + + torrent::this_thread::poll()->remove_read(this); + torrent::this_thread::scheduler()->update_wait_for(&m_resume_accept, std::chrono::seconds(1)); +} + +void +SCgi::resume_accept() { + if (!is_open()) + return; + + torrent::this_thread::poll()->insert_read(this); +} + void SCgi::event_write() { throw torrent::internal_error("Listener does not support write()."); diff --git a/src/rpc/scgi.h b/src/rpc/scgi.h index e0a50d7b..dcd2a803 100644 --- a/src/rpc/scgi.h +++ b/src/rpc/scgi.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "rpc/scgi_task.h" @@ -39,12 +40,16 @@ private: using task_list = std::array, max_tasks>; void open(sockaddr* sa, unsigned int length); + void pause_accept(int error); + void resume_accept(); std::string m_path; int m_logFd{-1}; task_list m_tasks; task_list::iterator m_current; + + torrent::system::SchedulerEntry m_resume_accept; }; }