xref: /unit/src/nxt_conn_close.c (revision 2185:2227bdbb3c89)
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         engine->closed_conns_cnt++;
123 
124         if (timers_pending == 0) {
125             nxt_work_queue_add(&engine->fast_work_queue,
126                                c->write_state->ready_handler,
127                                task, c, c->socket.data);
128             return;
129         }
130     }
131 
132     c->write_timer.handler = nxt_conn_close_timer_handler;
133     c->write_timer.work_queue = &engine->fast_work_queue;
134 
135     nxt_timer_add(engine, &c->write_timer, 0);
136 }
137 
138 
139 static void
140 nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data)
141 {
142     nxt_conn_t          *c;
143     nxt_timer_t         *timer;
144     nxt_event_engine_t  *engine;
145 
146     timer = obj;
147 
148     c = nxt_write_timer_conn(timer);
149 
150     nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd);
151 
152     engine = task->thread->engine;
153 
154     if (c->socket.fd != -1) {
155         nxt_socket_close(task, c->socket.fd);
156         c->socket.fd = -1;
157 
158         engine->closed_conns_cnt++;
159     }
160 
161     nxt_work_queue_add(&engine->fast_work_queue, c->write_state->ready_handler,
162                        task, c, c->socket.data);
163 }
164 
165 
166 static void
167 nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data)
168 {
169     nxt_debug(task, "conn close error ignore");
170 }
171