/src/unit/src/nxt_conn_close.c
Line | Count | Source |
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 | 0 | { |
21 | 0 | int ret; |
22 | 0 | nxt_work_queue_t *wq; |
23 | 0 | nxt_work_handler_t handler; |
24 | |
|
25 | 0 | static const struct linger linger_off = { |
26 | 0 | .l_onoff = 1, |
27 | 0 | .l_linger = 0, |
28 | 0 | }; |
29 | |
|
30 | 0 | nxt_debug(c->socket.task, "conn close fd:%d, to:%d", |
31 | 0 | 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 | 0 | c->write = NULL; |
38 | |
|
39 | 0 | 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 | 0 | ret = setsockopt(c->socket.fd, SOL_SOCKET, SO_LINGER, &linger_off, |
46 | 0 | sizeof(struct linger)); |
47 | |
|
48 | 0 | if (nxt_slow_path(ret != 0)) { |
49 | 0 | nxt_alert(c->socket.task, "setsockopt(%d, SO_LINGER) failed %E", |
50 | 0 | c->socket.fd, nxt_socket_errno); |
51 | 0 | } |
52 | 0 | } |
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 | 0 | c->socket.error_handler = nxt_conn_close_error_ignore; |
60 | |
|
61 | 0 | if (c->socket.error == 0 && !c->socket.closed && !c->socket.shutdown) { |
62 | 0 | wq = &engine->shutdown_work_queue; |
63 | 0 | handler = nxt_conn_shutdown_handler; |
64 | |
|
65 | 0 | } else { |
66 | 0 | wq = &engine->close_work_queue; |
67 | 0 | handler = nxt_conn_close_handler; |
68 | 0 | } |
69 | |
|
70 | 0 | nxt_work_queue_add(wq, handler, c->socket.task, c, engine); |
71 | 0 | } |
72 | | |
73 | | |
74 | | static void |
75 | | nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data) |
76 | 0 | { |
77 | 0 | nxt_conn_t *c; |
78 | 0 | nxt_event_engine_t *engine; |
79 | |
|
80 | 0 | c = obj; |
81 | 0 | engine = data; |
82 | |
|
83 | 0 | nxt_debug(task, "conn shutdown handler fd:%d", c->socket.fd); |
84 | |
|
85 | 0 | c->socket.shutdown = 1; |
86 | |
|
87 | 0 | nxt_socket_shutdown(task, c->socket.fd, SHUT_RDWR); |
88 | |
|
89 | 0 | nxt_work_queue_add(&engine->close_work_queue, nxt_conn_close_handler, |
90 | 0 | task, c, engine); |
91 | 0 | } |
92 | | |
93 | | |
94 | | static void |
95 | | nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data) |
96 | 0 | { |
97 | 0 | nxt_uint_t events_pending, timers_pending; |
98 | 0 | nxt_conn_t *c; |
99 | 0 | nxt_event_engine_t *engine; |
100 | |
|
101 | 0 | c = obj; |
102 | 0 | engine = data; |
103 | |
|
104 | 0 | 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 | 0 | timers_pending = nxt_timer_delete(engine, &c->read_timer); |
114 | 0 | timers_pending += nxt_timer_delete(engine, &c->write_timer); |
115 | |
|
116 | 0 | events_pending = nxt_fd_event_close(engine, &c->socket); |
117 | |
|
118 | 0 | if (events_pending == 0) { |
119 | 0 | nxt_socket_close(task, c->socket.fd); |
120 | 0 | c->socket.fd = -1; |
121 | |
|
122 | 0 | if (c->idle) { |
123 | 0 | engine->closed_conns_cnt++; |
124 | 0 | } |
125 | |
|
126 | 0 | if (timers_pending == 0) { |
127 | 0 | nxt_work_queue_add(&engine->fast_work_queue, |
128 | 0 | c->write_state->ready_handler, |
129 | 0 | task, c, c->socket.data); |
130 | 0 | return; |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | 0 | c->write_timer.handler = nxt_conn_close_timer_handler; |
135 | 0 | c->write_timer.work_queue = &engine->fast_work_queue; |
136 | |
|
137 | 0 | nxt_timer_add(engine, &c->write_timer, 0); |
138 | 0 | } |
139 | | |
140 | | |
141 | | static void |
142 | | nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data) |
143 | 0 | { |
144 | 0 | nxt_conn_t *c; |
145 | 0 | nxt_timer_t *timer; |
146 | 0 | nxt_event_engine_t *engine; |
147 | |
|
148 | 0 | timer = obj; |
149 | |
|
150 | 0 | c = nxt_write_timer_conn(timer); |
151 | |
|
152 | 0 | nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd); |
153 | |
|
154 | 0 | engine = task->thread->engine; |
155 | |
|
156 | 0 | if (c->socket.fd != -1) { |
157 | 0 | nxt_socket_close(task, c->socket.fd); |
158 | 0 | c->socket.fd = -1; |
159 | |
|
160 | 0 | if (c->idle) { |
161 | 0 | engine->closed_conns_cnt++; |
162 | 0 | } |
163 | 0 | } |
164 | |
|
165 | 0 | nxt_work_queue_add(&engine->fast_work_queue, c->write_state->ready_handler, |
166 | 0 | task, c, c->socket.data); |
167 | 0 | } |
168 | | |
169 | | |
170 | | static void |
171 | | nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data) |
172 | 0 | { |
173 | 0 | nxt_debug(task, "conn close error ignore"); |
174 | 0 | } |