1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 #include <nxt_main.h> 8 9 10 static void nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data); 11 static void nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data); 12 static void nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, 13 void *data); 14 static void nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, 15 void *data); 16 17 18 void 19 nxt_conn_close(nxt_event_engine_t *engine, nxt_conn_t *c) 20 { 21 int ret; 22 nxt_work_queue_t *wq; 23 nxt_work_handler_t handler; 24 25 static const struct linger linger_off = { 26 .l_onoff = 1, 27 .l_linger = 0, 28 }; 29 30 nxt_debug(c->socket.task, "conn close fd:%d, to:%d", 31 c->socket.fd, c->socket.timedout); 32 33 /* 34 * Disable all pending write operations because on success they 35 * will incorrectly call a ready handler set for nxt_conn_close(). 36 */ 37 c->write = NULL; 38 39 if (c->socket.timedout) { 40 /* 41 * Resetting of timed out connection on close 42 * releases kernel memory associated with socket. 43 * This also causes sending TCP/IP RST to a peer. 44 */ 45 ret = setsockopt(c->socket.fd, SOL_SOCKET, SO_LINGER, &linger_off, 46 sizeof(struct linger)); 47 48 if (nxt_slow_path(ret != 0)) { 49 nxt_alert(c->socket.task, "setsockopt(%d, SO_LINGER) failed %E", 50 c->socket.fd, nxt_socket_errno); 51 } 52 } 53 54 /* 55 * Event errors should be ignored here to avoid repeated nxt_conn_close() 56 * calls. nxt_conn_close_handler() or nxt_conn_close_timer_handler() 57 * will eventually close socket. 58 */ 59 c->socket.error_handler = nxt_conn_close_error_ignore; 60 61 if (c->socket.error == 0 && !c->socket.closed && !c->socket.shutdown) { 62 wq = &engine->shutdown_work_queue; 63 handler = nxt_conn_shutdown_handler; 64 65 } else { 66 wq = &engine->close_work_queue; 67 handler = nxt_conn_close_handler; 68 } 69 70 nxt_work_queue_add(wq, handler, c->socket.task, c, engine); 71 } 72 73 74 static void 75 nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data) 76 { 77 nxt_conn_t *c; 78 nxt_event_engine_t *engine; 79 80 c = obj; 81 engine = data; 82 83 nxt_debug(task, "conn shutdown handler fd:%d", c->socket.fd); 84 85 c->socket.shutdown = 1; 86 87 nxt_socket_shutdown(task, c->socket.fd, SHUT_RDWR); 88 89 nxt_work_queue_add(&engine->close_work_queue, nxt_conn_close_handler, 90 task, c, engine); 91 } 92 93 94 static void 95 nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data) 96 { 97 nxt_uint_t events_pending, timers_pending; 98 nxt_conn_t *c; 99 nxt_event_engine_t *engine; 100 101 c = obj; 102 engine = data; 103 104 nxt_debug(task, "conn close handler fd:%d", c->socket.fd); 105 106 /* 107 * Socket should be closed only after all pending socket event operations 108 * will be processed by the kernel. This could be achieved with zero-timer 109 * handler. Pending timer operations associated with the socket are 110 * processed before going to the kernel. 111 */ 112 113 timers_pending = nxt_timer_delete(engine, &c->read_timer); 114 timers_pending += nxt_timer_delete(engine, &c->write_timer); 115 116 events_pending = nxt_fd_event_close(engine, &c->socket); 117 118 if (events_pending == 0) { 119 nxt_socket_close(task, c->socket.fd); 120 c->socket.fd = -1; 121 122 if (timers_pending == 0) { 123 nxt_work_queue_add(&engine->fast_work_queue, 124 c->write_state->ready_handler, 125 task, c, c->socket.data); 126 return; 127 } 128 } 129 130 c->write_timer.handler = nxt_conn_close_timer_handler; 131 c->write_timer.work_queue = &engine->fast_work_queue; 132 133 nxt_timer_add(engine, &c->write_timer, 0); 134 } 135 136 137 static void 138 nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data) 139 { 140 nxt_conn_t *c; 141 nxt_timer_t *timer; 142 143 timer = obj; 144 145 c = nxt_write_timer_conn(timer); 146 147 nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd); 148 149 if (c->socket.fd != -1) { 150 nxt_socket_close(task, c->socket.fd); 151 c->socket.fd = -1; 152 } 153 154 nxt_work_queue_add(&task->thread->engine->fast_work_queue, 155 c->write_state->ready_handler, 156 task, c, c->socket.data); 157 } 158 159 160 static void 161 nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data) 162 { 163 nxt_debug(task, "conn close error ignore"); 164 } 165