xref: /unit/src/nxt_conn_close.c (revision 62:5e1efcc7b740)
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