Line data Source code
1 : /* 2 : * lwan - web server 3 : * Copyright (c) 2019 L. A. F. Pereira <l@tia.mat.br> 4 : * 5 : * This program is free software; you can redistribute it and/or 6 : * modify it under the terms of the GNU General Public License 7 : * as published by the Free Software Foundation; either version 2 8 : * of the License, or any later version. 9 : * 10 : * This program is distributed in the hope that it will be useful, 11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 : * GNU General Public License for more details. 14 : * 15 : * You should have received a copy of the GNU General Public License 16 : * along with this program; if not, write to the Free Software 17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 18 : * USA. 19 : */ 20 : 21 : #include <unistd.h> 22 : 23 : #include "lwan-private.h" 24 : #include "lwan-tq.h" 25 : 26 595 : static inline int timeout_queue_node_to_idx(struct timeout_queue *tq, 27 : struct lwan_connection *conn) 28 : { 29 595 : return (conn == &tq->head) ? -1 : (int)(intptr_t)(conn - tq->conns); 30 : } 31 : 32 : static inline struct lwan_connection * 33 1599 : timeout_queue_idx_to_node(struct timeout_queue *tq, int idx) 34 : { 35 1599 : return (idx < 0) ? &tq->head : &tq->conns[idx]; 36 : } 37 : 38 595 : inline void timeout_queue_insert(struct timeout_queue *tq, 39 : struct lwan_connection *new_node) 40 : { 41 595 : new_node->next = -1; 42 595 : new_node->prev = tq->head.prev; 43 595 : struct lwan_connection *prev = timeout_queue_idx_to_node(tq, tq->head.prev); 44 595 : tq->head.prev = prev->next = timeout_queue_node_to_idx(tq, new_node); 45 595 : } 46 : 47 500 : static inline void timeout_queue_remove(struct timeout_queue *tq, 48 : struct lwan_connection *node) 49 : { 50 500 : struct lwan_connection *prev = timeout_queue_idx_to_node(tq, node->prev); 51 500 : struct lwan_connection *next = timeout_queue_idx_to_node(tq, node->next); 52 : 53 500 : next->prev = node->prev; 54 500 : prev->next = node->next; 55 500 : } 56 : 57 13 : inline bool timeout_queue_empty(struct timeout_queue *tq) 58 : { 59 13 : return tq->head.next < 0; 60 : } 61 : 62 263 : inline void timeout_queue_move_to_last(struct timeout_queue *tq, 63 : struct lwan_connection *conn) 64 : { 65 : /* CONN_IS_KEEP_ALIVE isn't checked here because non-keep-alive connections 66 : * are closed in the request processing coroutine after they have been 67 : * served. In practice, if this is called, it's a keep-alive connection. */ 68 263 : conn->time_to_expire = tq->current_time + tq->move_to_last_bump; 69 : 70 263 : timeout_queue_remove(tq, conn); 71 263 : timeout_queue_insert(tq, conn); 72 263 : } 73 : 74 184 : void timeout_queue_init(struct timeout_queue *tq, const struct lwan *lwan) 75 : { 76 184 : *tq = (struct timeout_queue){ 77 : .lwan = lwan, 78 184 : .conns = lwan->conns, 79 : .current_time = 0, 80 184 : .move_to_last_bump = lwan->config.keep_alive_timeout, 81 : .head.next = -1, 82 : .head.prev = -1, 83 : .timeout = (struct timeout){}, 84 : }; 85 184 : } 86 : 87 237 : void timeout_queue_expire(struct timeout_queue *tq, 88 : struct lwan_connection *conn) 89 : { 90 237 : timeout_queue_remove(tq, conn); 91 : 92 237 : if (LIKELY(conn->coro)) { 93 237 : coro_free(conn->coro); 94 237 : conn->coro = NULL; 95 : } 96 : 97 237 : close(lwan_connection_get_fd(tq->lwan, conn)); 98 237 : } 99 : 100 6 : void timeout_queue_expire_waiting(struct timeout_queue *tq) 101 : { 102 6 : tq->current_time++; 103 : 104 7 : while (!timeout_queue_empty(tq)) { 105 : struct lwan_connection *conn = 106 4 : timeout_queue_idx_to_node(tq, tq->head.next); 107 : 108 4 : if (conn->time_to_expire > tq->current_time) 109 3 : return; 110 : 111 1 : timeout_queue_expire(tq, conn); 112 : } 113 : 114 : /* Timeout queue exhausted: reset epoch */ 115 3 : tq->current_time = 0; 116 : } 117 : 118 0 : void timeout_queue_expire_all(struct timeout_queue *tq) 119 : { 120 0 : while (!timeout_queue_empty(tq)) { 121 : struct lwan_connection *conn = 122 0 : timeout_queue_idx_to_node(tq, tq->head.next); 123 0 : timeout_queue_expire(tq, conn); 124 : } 125 0 : }