X-Git-Url: https://git.sesse.net/?p=cubemap;a=blobdiff_plain;f=server.cpp;h=079b629dcaa9b601b31033c3a4c55626711f7662;hp=6fa4991b209f389da7f940aa73ff9462f472b8cd;hb=ce7802e5d5291c414883efbad93a9d165c03f40c;hpb=0e96bbf9ee0fbebd5fe3fba4d186c0e0d73c9a32 diff --git a/server.cpp b/server.cpp index 6fa4991..079b629 100644 --- a/server.cpp +++ b/server.cpp @@ -35,6 +35,23 @@ using namespace std; extern AccessLogThread *access_log; +namespace { + +inline bool is_equal(timespec a, timespec b) +{ + return a.tv_sec == b.tv_sec && + a.tv_nsec == b.tv_nsec; +} + +inline bool is_earlier(timespec a, timespec b) +{ + if (a.tv_sec != b.tv_sec) + return a.tv_sec < b.tv_sec; + return a.tv_nsec < b.tv_nsec; +} + +} // namespace + Server::Server() { pthread_mutex_init(&mutex, NULL); @@ -90,6 +107,7 @@ void Server::do_work() process_queued_data(); + // Process each client where we have socket activity. for (int i = 0; i < nfds; ++i) { Client *client = reinterpret_cast(events[i].data.u64); @@ -101,6 +119,8 @@ void Server::do_work() process_client(client); } + // Process each client where its stream has new data, + // even if there was no socket activity. for (size_t i = 0; i < streams.size(); ++i) { vector to_process; swap(streams[i]->to_process, to_process); @@ -108,6 +128,49 @@ void Server::do_work() process_client(to_process[i]); } } + + // Finally, go through each client to see if it's timed out + // in the READING_REQUEST state. (Seemingly there are clients + // that can hold sockets up for days at a time without sending + // anything at all.) + timespec timeout_time; + if (clock_gettime(CLOCK_MONOTONIC_COARSE, &timeout_time) == -1) { + log_perror("clock_gettime(CLOCK_MONOTONIC_COARSE)"); + continue; + } + timeout_time.tv_sec -= REQUEST_READ_TIMEOUT_SEC; + while (!clients_ordered_by_connect_time.empty()) { + pair &connect_time_and_fd = clients_ordered_by_connect_time.front(); + + // See if we have reached the end of clients to process. + if (is_earlier(timeout_time, connect_time_and_fd.first)) { + break; + } + + // If this client doesn't exist anymore, just ignore it + // (it was deleted earlier). + std::map::iterator client_it = clients.find(connect_time_and_fd.second); + if (client_it == clients.end()) { + clients_ordered_by_connect_time.pop(); + continue; + } + Client *client = &client_it->second; + if (!is_equal(client->connect_time, connect_time_and_fd.first)) { + // Another client has taken this fd in the meantime. + clients_ordered_by_connect_time.pop(); + continue; + } + + if (client->state != Client::READING_REQUEST) { + // Only READING_REQUEST can time out. + clients_ordered_by_connect_time.pop(); + continue; + } + + // OK, it timed out. + close_client(client); + clients_ordered_by_connect_time.pop(); + } } } @@ -154,6 +217,17 @@ void Server::add_client(int sock) assert(ret.second == true); // Should not already exist. Client *client_ptr = &ret.first->second; + // Connection timestamps must be nondecreasing. I can't find any guarantee + // that even the monotonic clock can't go backwards by a small amount + // (think switching between CPUs with non-synchronized TSCs), so if + // this actually should happen, we hack around it by fudging + // connect_time. + if (!clients_ordered_by_connect_time.empty() && + is_earlier(client_ptr->connect_time, clients_ordered_by_connect_time.back().first)) { + client_ptr->connect_time = clients_ordered_by_connect_time.back().first; + } + clients_ordered_by_connect_time.push(make_pair(client_ptr->connect_time, sock)); + // Start listening on data from this socket. epoll_event ev; ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP; @@ -182,6 +256,11 @@ void Server::add_client_from_serialized(const ClientProto &client) assert(ret.second == true); // Should not already exist. Client *client_ptr = &ret.first->second; + // Connection timestamps must be nondecreasing. + assert(clients_ordered_by_connect_time.empty() || + !is_earlier(client_ptr->connect_time, clients_ordered_by_connect_time.back().first)); + clients_ordered_by_connect_time.push(make_pair(client_ptr->connect_time, client.sock())); + // Start listening on data from this socket. epoll_event ev; if (client.state() == Client::READING_REQUEST) {