xref: /unit/src/nxt_conn_close.c (revision 521:93dc4a28dd37)
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_log(c->socket.task, NXT_LOG_CRIT,
50                     "setsockopt(%d, SO_LINGER) failed %E",
51                     c->socket.fd, nxt_socket_errno);
52         }
53     }
54 
55     /*
56      * Event errors should be ignored here to avoid repeated nxt_conn_close()
57      * calls.  nxt_conn_close_handler() or nxt_conn_close_timer_handler()
58      * will eventually close socket.
59      */
60     c->socket.error_handler = nxt_conn_close_error_ignore;
61 
62     if (c->socket.error == 0 && !c->socket.closed && !c->socket.shutdown) {
63         wq = &engine->shutdown_work_queue;
64         handler = nxt_conn_shutdown_handler;
65 
66     } else {
67         wq = &engine->close_work_queue;
68         handler = nxt_conn_close_handler;
69     }
70 
71     nxt_work_queue_add(wq, handler, c->socket.task, c, engine);
72 }
73 
74 
75 static void
76 nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data)
77 {
78     nxt_conn_t          *c;
79     nxt_event_engine_t  *engine;
80 
81     c = obj;
82     engine = data;
83 
84     nxt_debug(task, "conn shutdown handler fd:%d", c->socket.fd);
85 
86     c->socket.shutdown = 1;
87 
88     nxt_socket_shutdown(task, c->socket.fd, SHUT_RDWR);
89 
90     nxt_work_queue_add(&engine->close_work_queue, nxt_conn_close_handler,
91                        task, c, engine);
92 }
93 
94 
95 static void
96 nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data)
97 {
98     nxt_uint_t          events_pending, timers_pending;
99     nxt_conn_t          *c;
100     nxt_event_engine_t  *engine;
101 
102     c = obj;
103     engine = data;
104 
105     nxt_debug(task, "conn close handler fd:%d", c->socket.fd);
106 
107     /*
108      * Socket should be closed only after all pending socket event operations
109      * will be processed by the kernel.  This could be achieved with zero-timer
110      * handler.  Pending timer operations associated with the socket are
111      * processed before going to the kernel.
112      */
113 
114     timers_pending = nxt_timer_delete(engine, &c->read_timer);
115     timers_pending += nxt_timer_delete(engine, &c->write_timer);
116 
117     events_pending = nxt_fd_event_close(engine, &c->socket);
118 
119     if (events_pending == 0) {
120         nxt_socket_close(task, c->socket.fd);
121         c->socket.fd = -1;
122 
123         if (timers_pending == 0) {
124             nxt_work_queue_add(&engine->fast_work_queue,
125                                c->write_state->ready_handler,
126                                task, c, c->socket.data);
127             return;
128         }
129     }
130 
131     c->write_timer.handler = nxt_conn_close_timer_handler;
132     c->write_timer.work_queue = &engine->fast_work_queue;
133 
134     nxt_timer_add(engine, &c->write_timer, 0);
135 }
136 
137 
138 static void
139 nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data)
140 {
141     nxt_conn_t   *c;
142     nxt_timer_t  *timer;
143 
144     timer = obj;
145 
146     c = nxt_write_timer_conn(timer);
147 
148     nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd);
149 
150     if (c->socket.fd != -1) {
151         nxt_socket_close(task, c->socket.fd);
152         c->socket.fd = -1;
153     }
154 
155     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
156                        c->write_state->ready_handler,
157                        task, c, c->socket.data);
158 }
159 
160 
161 static void
162 nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data)
163 {
164     nxt_debug(task, "conn close error ignore");
165 }
166