xref: /unit/src/nxt_conn_close.c (revision 564:762f8c976ead)
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
nxt_conn_close(nxt_event_engine_t * engine,nxt_conn_t * c)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
nxt_conn_shutdown_handler(nxt_task_t * task,void * obj,void * data)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
nxt_conn_close_handler(nxt_task_t * task,void * obj,void * data)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
nxt_conn_close_timer_handler(nxt_task_t * task,void * obj,void * data)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
nxt_conn_close_error_ignore(nxt_task_t * task,void * obj,void * data)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