| 1088 | ssize_t n = ::send(fd, ping, sizeof(ping) - 1, MSG_NOSIGNAL | MSG_DONTWAIT); |
| 1089 | if (n <= 0) { |
| 1090 | dead.push_back(fd); |
| 1091 | } |
| 1092 | } |
| 1093 | for (SocketHandle fd : dead) { |
| 1094 | socket_close(fd); |
| 1095 | sse_fds_.erase(std::remove(sse_fds_.begin(), sse_fds_.end(), fd), |
| 1096 | sse_fds_.end()); |
| 1097 | } |
| 1098 | } |
| 1099 | |
| 1100 | HttpServer::~HttpServer() { |
| 1101 | shutdown(); |
| 1102 | #ifdef DFLASH_HAS_CURL |
| 1103 | curl_global_cleanup(); |
| 1104 | #endif |
| 1105 | } |
| 1106 | |
| 1107 | void HttpServer::shutdown() { |
| 1108 | // Signal worker and accept loop to stop. |
| 1109 | stopping_.store(true); |
| 1110 | queue_cv_.notify_all(); |
| 1111 | if (socket_is_valid(listen_fd_)) { |
| 1112 | socket_close(listen_fd_); |
| 1113 | listen_fd_ = kInvalidSocket; |
| 1114 | } |
| 1115 | if (worker_thread_.joinable()) { |
| 1116 | worker_thread_.join(); |
| 1117 | } |
| 1118 | |
| 1119 | // Close SSE client connections. |
| 1120 | { |
| 1121 | std::lock_guard<std::mutex> lk(sse_mu_); |
| 1122 | for (SocketHandle fd : sse_fds_) socket_close(fd); |
| 1123 | sse_fds_.clear(); |
| 1124 | } |
| 1125 | |
| 1126 | // Drain any pending jobs. |
| 1127 | { |
| 1128 | std::lock_guard<std::mutex> lk(queue_mu_); |
| 1129 | while (queue_head_) { |
| 1130 | ServerJob * j = queue_head_; |
| 1131 | queue_head_ = j->next; |
| 1132 | j->next = nullptr; |
| 1133 | std::lock_guard<std::mutex> jlk(j->mu); |
| 1134 | j->done = true; |
| 1135 | j->cv.notify_one(); |
| 1136 | } |
| 1137 | queue_tail_ = nullptr; |
| 1138 | } |
| 1139 | |
| 1140 | // Shutdown save: persist all tracked snapshot slots to disk. |
| 1141 | // Safe to access slot_tokens_ without locking — worker is joined. |
| 1142 | if (!disk_cache_.disabled() && !slot_tokens_.empty()) { |
| 1143 | std::fprintf(stderr, "[disk-cache] shutdown: saving %zu tracked slots\n", |
| 1144 | slot_tokens_.size()); |
| 1145 | for (auto & [slot, tokens] : slot_tokens_) { |
| 1146 | if (backend_.snapshot_used(slot)) { |
| 1147 | disk_cache_.learn_layout(slot); |