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