From 914a7e14e2af8260a1b0d797cc95e0552ccae53f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
Date: Wed, 2 Feb 2022 10:50:27 +0100
Subject: [PATCH] Add network manager based timer API

This commits adds API that allows to create arbitrary timers associated
with the network manager handles.

(cherry picked from commit 3c7b04d0150ae6d6192747d90d52247bd598bd9a)
Conflict: UV_RUNTIME_CHECK to RUNTIME_CHECK
Reference: https://gitlab.isc.org/isc-projects/bind9/-/commit/914a7e14e2af8260a1b0d797cc95e0552ccae53f
---
 lib/isc/include/isc/netmgr.h | 24 ++++++++++
 lib/isc/netmgr/netmgr-int.h  |  8 ++++
 lib/isc/netmgr/netmgr.c      | 91 ++++++++++++++++++++++++++++++++++++
 lib/isc/win32/libisc.def.in  |  5 ++
 4 files changed, 128 insertions(+)

diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h
index 39dba1d376..7ec40e81b6 100644
--- a/lib/isc/include/isc/netmgr.h
+++ b/lib/isc/include/isc/netmgr.h
@@ -492,3 +492,27 @@ isc__nm_force_tid(int tid);
 
 void
 isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout);
+
+/*
+ * Timer related functions
+ */
+
+typedef struct isc_nm_timer isc_nm_timer_t;
+
+typedef void (*isc_nm_timer_cb)(void *, isc_result_t);
+
+void
+isc_nm_timer_create(isc_nmhandle_t *, isc_nm_timer_cb, void *,
+		    isc_nm_timer_t **);
+
+void
+isc_nm_timer_attach(isc_nm_timer_t *, isc_nm_timer_t **);
+
+void
+isc_nm_timer_detach(isc_nm_timer_t **);
+
+void
+isc_nm_timer_start(isc_nm_timer_t *, uint64_t);
+
+void
+isc_nm_timer_stop(isc_nm_timer_t *);
diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h
index 3871d70939..23b197179a 100644
--- a/lib/isc/netmgr/netmgr-int.h
+++ b/lib/isc/netmgr/netmgr-int.h
@@ -350,6 +350,14 @@ struct isc__nm_uvreq {
 	ISC_LINK(isc__nm_uvreq_t) link;
 };
 
+struct isc_nm_timer {
+	isc_refcount_t references;
+	uv_timer_t timer;
+	isc_nmhandle_t *handle;
+	isc_nm_timer_cb cb;
+	void *cbarg;
+};
+
 void *
 isc__nm_get_netievent(isc_nm_t *mgr, isc__netievent_type type);
 /*%<
diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c
index 31917be101..fb9d77d3b4 100644
--- a/lib/isc/netmgr/netmgr.c
+++ b/lib/isc/netmgr/netmgr.c
@@ -3179,6 +3179,97 @@ isc_nm_work_offload(isc_nm_t *netmgr, isc_nm_workcb_t work_cb,
 	RUNTIME_CHECK(r == 0);
 }
 
+void
+isc_nm_timer_create(isc_nmhandle_t *handle, isc_nm_timer_cb cb, void *cbarg,
+		    isc_nm_timer_t **timerp) {
+	isc__networker_t *worker = NULL;
+	isc_nmsocket_t *sock = NULL;
+	isc_nm_timer_t *timer = NULL;
+	int r;
+
+	REQUIRE(isc__nm_in_netthread());
+	REQUIRE(VALID_NMHANDLE(handle));
+	REQUIRE(VALID_NMSOCK(handle->sock));
+
+	sock = handle->sock;
+	worker = &sock->mgr->workers[isc_nm_tid()];
+
+	timer = isc_mem_get(sock->mgr->mctx, sizeof(*timer));
+	*timer = (isc_nm_timer_t){ .cb = cb, .cbarg = cbarg };
+	isc_refcount_init(&timer->references, 1);
+	isc_nmhandle_attach(handle, &timer->handle);
+
+	r = uv_timer_init(&worker->loop, &timer->timer);
+	UV_RUNTIME_CHECK(uv_timer_init, r);
+
+	uv_handle_set_data((uv_handle_t *)&timer->timer, timer);
+
+	*timerp = timer;
+}
+
+void
+isc_nm_timer_attach(isc_nm_timer_t *timer, isc_nm_timer_t **timerp) {
+	REQUIRE(timer != NULL);
+	REQUIRE(timerp != NULL && *timerp == NULL);
+
+	isc_refcount_increment(&timer->references);
+	*timerp = timer;
+}
+
+static void
+timer_destroy(uv_handle_t *uvhandle) {
+	isc_nm_timer_t *timer = uv_handle_get_data(uvhandle);
+	isc_nmhandle_t *handle = timer->handle;
+	isc_mem_t *mctx = timer->handle->sock->mgr->mctx;
+
+	isc_mem_put(mctx, timer, sizeof(*timer));
+
+	isc_nmhandle_detach(&handle);
+}
+
+void
+isc_nm_timer_detach(isc_nm_timer_t **timerp) {
+	isc_nm_timer_t *timer = NULL;
+	isc_nmhandle_t *handle = NULL;
+
+	REQUIRE(timerp != NULL && *timerp != NULL);
+
+	timer = *timerp;
+	*timerp = NULL;
+
+	handle = timer->handle;
+
+	REQUIRE(isc__nm_in_netthread());
+	REQUIRE(VALID_NMHANDLE(handle));
+	REQUIRE(VALID_NMSOCK(handle->sock));
+
+	if (isc_refcount_decrement(&timer->references) == 1) {
+		uv_timer_stop(&timer->timer);
+		uv_close((uv_handle_t *)&timer->timer, timer_destroy);
+	}
+}
+
+static void
+timer_cb(uv_timer_t *uvtimer) {
+	isc_nm_timer_t *timer = uv_handle_get_data((uv_handle_t *)uvtimer);
+
+	REQUIRE(timer->cb != NULL);
+
+	timer->cb(timer->cbarg, ISC_R_TIMEDOUT);
+}
+
+void
+isc_nm_timer_start(isc_nm_timer_t *timer, uint64_t timeout) {
+	int r = uv_timer_start(&timer->timer, timer_cb, timeout, 0);
+	UV_RUNTIME_CHECK(uv_timer_start, r);
+}
+
+void
+isc_nm_timer_stop(isc_nm_timer_t *timer) {
+	int r = uv_timer_stop(&timer->timer);
+	UV_RUNTIME_CHECK(uv_timer_stop, r);
+}
+
 #ifdef NETMGR_TRACE
 /*
  * Dump all active sockets in netmgr. We output to stderr
diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in
index 79e7b64a58..ab7ad73d20 100644
--- a/lib/isc/win32/libisc.def.in
+++ b/lib/isc/win32/libisc.def.in
@@ -480,6 +480,11 @@ isc_nm_tcpconnect
 isc_nm_tcpdnsconnect
 isc_nm_tcpdns_sequential
 isc_nm_tid
+isc_nm_timer_create
+isc_nm_timer_attach
+isc_nm_timer_detach
+isc_nm_timer_start
+isc_nm_timer_stop
 isc_nm_udpconnect
 isc_nm_work_offload
 isc_nmsocket_close
-- 
2.27.0