diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 4c84047e23..188d90340e 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -954,6 +954,13 @@ struct isc_nmsocket { uint64_t read_timeout; uint64_t connect_timeout; + /*% + * TCP write timeout timer. + */ + uv_timer_t write_timer; + uint64_t write_timeout; + int64_t writes; + /*% outer socket is for 'wrapped' sockets - e.g. tcpdns in tcp */ isc_nmsocket_t *outer; @@ -2071,12 +2078,25 @@ isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, void isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); -void -isc__nmsocket_connecttimeout_cb(uv_timer_t *timer); - void isc__nm_accept_connection_log(isc_result_t result, bool can_log_quota); +/* + * Timeout callbacks + */ +void +isc__nmsocket_connecttimeout_cb(uv_timer_t *timer); +void +isc__nmsocket_readtimeout_cb(uv_timer_t *timer); +void +isc__nmsocket_writetimeout_cb(uv_timer_t *timer); + +/*%< + * + * Maximum number of simultaneous handles in flight supported for a single + * connected TCPDNS socket. This value was chosen arbitrarily, and may be + * changed in the future. + */ #define STREAM_CLIENTS_PER_CONN 23 #define UV_RUNTIME_CHECK(func, ret) \ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 1574ad2a31..b63f990163 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -2070,7 +2070,21 @@ isc__nm_accept_connection_log(isc_result_t result, bool can_log_quota) { isc_result_totext(result)); } -static void +void +isc__nmsocket_writetimeout_cb(uv_timer_t *timer) { + isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer); + + int r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + + /* The shutdown will be handled in the respective close functions */ + r = uv_tcp_close_reset(&sock->uv_handle.tcp, NULL); + UV_RUNTIME_CHECK(uv_tcp_close_reset, r); + + isc__nmsocket_shutdown(sock); +} + +void isc__nmsocket_readtimeout_cb(uv_timer_t *timer) { isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer); @@ -2447,6 +2461,8 @@ isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) { atomic_store(&sock->keepalive, value); sock->read_timeout = value ? atomic_load(&sock->mgr->keepalive) : atomic_load(&sock->mgr->idle); + sock->write_timeout = value ? atomic_load(&sock->mgr->keepalive) + : atomic_load(&sock->mgr->idle); break; #if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index aef698a79d..e179bc05b0 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -144,6 +144,10 @@ tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); if (r != 0) { isc__nm_closesocket(sock->fd); @@ -533,6 +537,10 @@ isc__nm_async_tcplisten(isc__networker_t *worker, isc__netievent_t *ev0) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + LOCK(&sock->parent->lock); r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); @@ -979,6 +987,10 @@ accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock); + r = uv_timer_init(&worker->loop, &csock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&csock->write_timer, csock); + r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream); if (r != 0) { result = isc__nm_uverr2result(r); @@ -1072,6 +1084,13 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, uvreq->cb.send = cb; uvreq->cbarg = cbarg; + if (sock->write_timeout == 0) { + sock->write_timeout = + (atomic_load(&sock->keepalive) + ? atomic_load(&sock->mgr->keepalive) + : atomic_load(&sock->mgr->idle)); + } + ievent = isc__nm_get_netievent_tcpsend(sock->mgr, sock, uvreq); isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid], (isc__netievent_t *)ievent); @@ -1082,11 +1101,17 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, static void tcp_send_cb(uv_write_t *req, int status) { isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; + REQUIRE(VALID_UVREQ(uvreq)); REQUIRE(VALID_NMHANDLE(uvreq->handle)); isc_nmsocket_t *sock = uvreq->sock; + if (--sock->writes == 0) { + int r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + } + if (status < 0) { isc__nm_incstats(sock, STATID_SENDFAIL); failed_send_cb(sock, uvreq, isc__nm_uverr2result(status)); @@ -1130,6 +1155,11 @@ tcp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { return (ISC_R_CANCELED); } + r = uv_timer_start(&sock->write_timer, isc__nmsocket_writetimeout_cb, + sock->write_timeout, 0); + UV_RUNTIME_CHECK(uv_timer_start, r); + RUNTIME_CHECK(sock->writes++ >= 0); + r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, &req->uvbuf, 1, tcp_send_cb); if (r < 0) { @@ -1193,7 +1223,7 @@ tcp_close_cb(uv_handle_t *handle) { } static void -timer_close_cb(uv_handle_t *handle) { +read_timer_close_cb(uv_handle_t *handle) { isc_nmsocket_t *sock = uv_handle_get_data(handle); uv_handle_set_data(handle, NULL); @@ -1206,6 +1236,17 @@ timer_close_cb(uv_handle_t *handle) { } } +static void +write_timer_close_cb(uv_handle_t *timer) { + isc_nmsocket_t *sock = uv_handle_get_data(timer); + uv_handle_set_data(timer, NULL); + + REQUIRE(VALID_NMSOCK(sock)); + + uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb); +} + static void stop_tcp_child(isc_nmsocket_t *sock) { REQUIRE(sock->type == isc_nm_tcpsocket); @@ -1258,6 +1299,8 @@ stop_tcp_parent(isc_nmsocket_t *sock) { static void tcp_close_direct(isc_nmsocket_t *sock) { + int r; + REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(atomic_load(&sock->closing)); @@ -1279,8 +1322,10 @@ tcp_close_direct(isc_nmsocket_t *sock) { isc__nmsocket_timer_stop(sock); isc__nm_stop_reading(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - uv_close((uv_handle_t *)&sock->read_timer, timer_close_cb); + r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + uv_close((uv_handle_t *)&sock->write_timer, write_timer_close_cb); } void diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c index ac8fbca33c..9244320ec0 100644 --- a/lib/isc/netmgr/tcpdns.c +++ b/lib/isc/netmgr/tcpdns.c @@ -37,13 +37,6 @@ #include "netmgr-int.h" #include "uv-compat.h" -/*%< - * - * Maximum number of simultaneous handles in flight supported for a single - * connected TCPDNS socket. This value was chosen arbitrarily, and may be - * changed in the future. - */ - static atomic_uint_fast32_t last_tcpdnsquota_log = ATOMIC_VAR_INIT(0); static bool @@ -109,6 +102,10 @@ tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + if (isc__nm_closing(sock)) { result = ISC_R_SHUTTINGDOWN; goto error; @@ -503,6 +500,10 @@ isc__nm_async_tcpdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + LOCK(&sock->parent->lock); r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); @@ -972,6 +973,10 @@ accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock); + r = uv_timer_init(&worker->loop, &csock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&csock->write_timer, csock); + r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream); if (r != 0) { result = isc__nm_uverr2result(r); @@ -1088,6 +1093,13 @@ isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region, uvreq->cb.send = cb; uvreq->cbarg = cbarg; + if (sock->write_timeout == 0) { + sock->write_timeout = + (atomic_load(&sock->keepalive) + ? atomic_load(&sock->mgr->keepalive) + : atomic_load(&sock->mgr->idle)); + } + ievent = isc__nm_get_netievent_tcpdnssend(sock->mgr, sock, uvreq); isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid], (isc__netievent_t *)ievent); @@ -1105,6 +1117,11 @@ tcpdns_send_cb(uv_write_t *req, int status) { sock = uvreq->sock; + if (--sock->writes == 0) { + int r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + } + if (status < 0) { isc__nm_incstats(sock, STATID_SENDFAIL); isc__nm_failed_send_cb(sock, uvreq, @@ -1171,6 +1188,11 @@ isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { goto fail; } + r = uv_timer_start(&sock->write_timer, isc__nmsocket_writetimeout_cb, + sock->write_timeout, 0); + UV_RUNTIME_CHECK(uv_timer_start, r); + RUNTIME_CHECK(sock->writes++ >= 0); + r = uv_write(&uvreq->uv_req.write, &sock->uv_handle.stream, bufs, nbufs, tcpdns_send_cb); if (r < 0) { @@ -1240,7 +1262,7 @@ tcpdns_close_cb(uv_handle_t *handle) { } static void -timer_close_cb(uv_handle_t *timer) { +read_timer_close_cb(uv_handle_t *timer) { isc_nmsocket_t *sock = uv_handle_get_data(timer); uv_handle_set_data(timer, NULL); @@ -1255,6 +1277,17 @@ timer_close_cb(uv_handle_t *timer) { } } +static void +write_timer_close_cb(uv_handle_t *timer) { + isc_nmsocket_t *sock = uv_handle_get_data(timer); + uv_handle_set_data(timer, NULL); + + REQUIRE(VALID_NMSOCK(sock)); + + uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb); +} + static void stop_tcpdns_child(isc_nmsocket_t *sock) { REQUIRE(sock->type == isc_nm_tcpdnssocket); @@ -1307,6 +1340,7 @@ stop_tcpdns_parent(isc_nmsocket_t *sock) { static void tcpdns_close_direct(isc_nmsocket_t *sock) { + int r; REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(atomic_load(&sock->closing)); @@ -1322,8 +1356,10 @@ tcpdns_close_direct(isc_nmsocket_t *sock) { isc__nmsocket_timer_stop(sock); isc__nm_stop_reading(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - uv_close((uv_handle_t *)&sock->read_timer, timer_close_cb); + r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + uv_close((uv_handle_t *)&sock->write_timer, write_timer_close_cb); } void diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index fc61e918aa..d91e04489d 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -38,13 +38,6 @@ #include "openssl_shim.h" #include "uv-compat.h" -/*%< - * - * Maximum number of simultaneous handles in flight supported for a single - * connected TLSDNS socket. This value was chosen arbitrarily, and may be - * changed in the future. - */ - static atomic_uint_fast32_t last_tlsdnsquota_log = ATOMIC_VAR_INIT(0); static void @@ -125,6 +118,10 @@ tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + if (isc__nm_closing(sock)) { result = ISC_R_SHUTTINGDOWN; goto error; @@ -574,6 +571,10 @@ isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + LOCK(&sock->parent->lock); r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); @@ -1165,6 +1166,11 @@ tls_write_cb(uv_write_t *req, int status) { isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; isc_nmsocket_t *sock = uvreq->sock; + if (--sock->writes == 0) { + int r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + } + free_senddata(sock); isc__nm_uvreq_put(&uvreq, sock); @@ -1238,6 +1244,12 @@ tls_cycle_output(isc_nmsocket_t *sock) { break; } + r = uv_timer_start(&sock->write_timer, + isc__nmsocket_writetimeout_cb, + sock->write_timeout, 0); + UV_RUNTIME_CHECK(uv_timer_start, r); + RUNTIME_CHECK(sock->writes++ >= 0); + r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, &req->uvbuf, 1, tls_write_cb); if (r < 0) { @@ -1482,6 +1494,10 @@ accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock); + r = uv_timer_init(&worker->loop, &csock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&csock->write_timer, csock); + r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream); if (r != 0) { result = isc__nm_uverr2result(r); @@ -1629,6 +1645,13 @@ isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region, uvreq->cb.send = cb; uvreq->cbarg = cbarg; + if (sock->write_timeout == 0) { + sock->write_timeout = + (atomic_load(&sock->keepalive) + ? atomic_load(&sock->mgr->keepalive) + : atomic_load(&sock->mgr->idle)); + } + ievent = isc__nm_get_netievent_tlsdnssend(sock->mgr, sock, uvreq); isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], (isc__netievent_t *)ievent); @@ -1806,7 +1829,7 @@ tlsdns_close_cb(uv_handle_t *handle) { } static void -timer_close_cb(uv_handle_t *handle) { +read_timer_close_cb(uv_handle_t *handle) { isc_nmsocket_t *sock = uv_handle_get_data(handle); uv_handle_set_data(handle, NULL); @@ -1821,6 +1844,17 @@ timer_close_cb(uv_handle_t *handle) { } } +static void +write_timer_close_cb(uv_handle_t *timer) { + isc_nmsocket_t *sock = uv_handle_get_data(timer); + uv_handle_set_data(timer, NULL); + + REQUIRE(VALID_NMSOCK(sock)); + + uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb); +} + static void stop_tlsdns_child(isc_nmsocket_t *sock) { REQUIRE(sock->type == isc_nm_tlsdnssocket); @@ -1874,6 +1908,8 @@ stop_tlsdns_parent(isc_nmsocket_t *sock) { static void tlsdns_close_direct(isc_nmsocket_t *sock) { + int r; + REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->tid == isc_nm_tid()); REQUIRE(atomic_load(&sock->closing)); @@ -1891,8 +1927,10 @@ tlsdns_close_direct(isc_nmsocket_t *sock) { isc__nmsocket_timer_stop(sock); isc__nm_stop_reading(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - uv_close((uv_handle_t *)&sock->read_timer, timer_close_cb); + r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + uv_close((uv_handle_t *)&sock->write_timer, write_timer_close_cb); } void diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 7d8c5f4b9a..e8ef4100f1 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -74,7 +74,10 @@ static void udp_close_cb(uv_handle_t *handle); static void -timer_close_cb(uv_handle_t *handle); +read_timer_close_cb(uv_handle_t *handle); + +static void +write_timer_close_cb(uv_handle_t *handle); static void udp_close_direct(isc_nmsocket_t *sock); @@ -269,6 +272,10 @@ route_connect_direct(isc_nmsocket_t *sock) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + if (isc__nm_closing(sock)) { result = ISC_R_SHUTTINGDOWN; goto error; @@ -445,6 +452,10 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + LOCK(&sock->parent->lock); r = uv_udp_open(&sock->uv_handle.udp, sock->fd); @@ -861,6 +872,10 @@ udp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { UV_RUNTIME_CHECK(uv_timer_init, r); uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + r = uv_timer_init(&worker->loop, &sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_init, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + if (isc__nm_closing(sock)) { result = ISC_R_SHUTTINGDOWN; goto error; @@ -1210,7 +1225,7 @@ udp_close_cb(uv_handle_t *handle) { } static void -timer_close_cb(uv_handle_t *handle) { +read_timer_close_cb(uv_handle_t *handle) { isc_nmsocket_t *sock = uv_handle_get_data(handle); uv_handle_set_data(handle, NULL); @@ -1221,6 +1236,17 @@ timer_close_cb(uv_handle_t *handle) { } } +static void +write_timer_close_cb(uv_handle_t *timer) { + isc_nmsocket_t *sock = uv_handle_get_data(timer); + uv_handle_set_data(timer, NULL); + + REQUIRE(VALID_NMSOCK(sock)); + + uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); + uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb); +} + static void stop_udp_child(isc_nmsocket_t *sock) { REQUIRE(sock->type == isc_nm_udpsocket); @@ -1273,10 +1299,14 @@ stop_udp_parent(isc_nmsocket_t *sock) { static void udp_close_direct(isc_nmsocket_t *sock) { + int r; REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->tid == isc_nm_tid()); - uv_close((uv_handle_t *)&sock->read_timer, timer_close_cb); + r = uv_timer_stop(&sock->write_timer); + UV_RUNTIME_CHECK(uv_timer_stop, r); + uv_handle_set_data((uv_handle_t *)&sock->write_timer, sock); + uv_close((uv_handle_t *)&sock->write_timer, write_timer_close_cb); } void