xref: /netbsd-src/external/mpl/bind/dist/lib/isc/timer.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: timer.c,v 1.15 2025/01/26 16:25:39 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <stdbool.h>
19 
20 #include <isc/async.h>
21 #include <isc/condition.h>
22 #include <isc/heap.h>
23 #include <isc/job.h>
24 #include <isc/log.h>
25 #include <isc/magic.h>
26 #include <isc/mem.h>
27 #include <isc/once.h>
28 #include <isc/refcount.h>
29 #include <isc/thread.h>
30 #include <isc/time.h>
31 #include <isc/timer.h>
32 #include <isc/util.h>
33 #include <isc/uv.h>
34 
35 #include "loop_p.h"
36 
37 #define TIMER_MAGIC    ISC_MAGIC('T', 'I', 'M', 'R')
38 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
39 
40 struct isc_timer {
41 	unsigned int magic;
42 	isc_loop_t *loop;
43 	uv_timer_t timer;
44 	isc_job_cb cb;
45 	void *cbarg;
46 	uint64_t timeout;
47 	uint64_t repeat;
48 	atomic_bool running;
49 };
50 
51 void
52 isc_timer_create(isc_loop_t *loop, isc_job_cb cb, void *cbarg,
53 		 isc_timer_t **timerp) {
54 	int r;
55 	isc_timer_t *timer;
56 	isc_loopmgr_t *loopmgr = NULL;
57 
58 	REQUIRE(cb != NULL);
59 	REQUIRE(timerp != NULL && *timerp == NULL);
60 
61 	REQUIRE(VALID_LOOP(loop));
62 
63 	loopmgr = loop->loopmgr;
64 
65 	REQUIRE(VALID_LOOPMGR(loopmgr));
66 	REQUIRE(loop == isc_loop());
67 
68 	timer = isc_mem_get(loop->mctx, sizeof(*timer));
69 	*timer = (isc_timer_t){
70 		.cb = cb,
71 		.cbarg = cbarg,
72 		.magic = TIMER_MAGIC,
73 	};
74 
75 	isc_loop_attach(loop, &timer->loop);
76 
77 	r = uv_timer_init(&loop->loop, &timer->timer);
78 	UV_RUNTIME_CHECK(uv_timer_init, r);
79 	uv_handle_set_data(&timer->timer, timer);
80 
81 	*timerp = timer;
82 }
83 
84 void
85 isc_timer_stop(isc_timer_t *timer) {
86 	REQUIRE(VALID_TIMER(timer));
87 
88 	if (!atomic_compare_exchange_strong_acq_rel(&timer->running,
89 						    &(bool){ true }, false))
90 	{
91 		/* Timer was already stopped */
92 		return;
93 	}
94 
95 	/* Stop the timer, if the loops are matching */
96 	if (timer->loop == isc_loop()) {
97 		uv_timer_stop(&timer->timer);
98 	}
99 }
100 
101 static void
102 timer_cb(uv_timer_t *handle) {
103 	isc_timer_t *timer = uv_handle_get_data(handle);
104 
105 	REQUIRE(VALID_TIMER(timer));
106 
107 	if (!atomic_load_acquire(&timer->running)) {
108 		uv_timer_stop(&timer->timer);
109 		return;
110 	}
111 
112 	timer->cb(timer->cbarg);
113 }
114 
115 void
116 isc_timer_start(isc_timer_t *timer, isc_timertype_t type,
117 		const isc_interval_t *interval) {
118 	isc_loopmgr_t *loopmgr = NULL;
119 	isc_loop_t *loop = NULL;
120 	int r;
121 
122 	REQUIRE(VALID_TIMER(timer));
123 	REQUIRE(type == isc_timertype_ticker || type == isc_timertype_once);
124 	REQUIRE(timer->loop == isc_loop());
125 
126 	loop = timer->loop;
127 
128 	REQUIRE(VALID_LOOP(loop));
129 
130 	loopmgr = loop->loopmgr;
131 
132 	REQUIRE(VALID_LOOPMGR(loopmgr));
133 
134 	switch (type) {
135 	case isc_timertype_once:
136 		timer->timeout = isc_interval_ms(interval);
137 		timer->repeat = 0;
138 		break;
139 	case isc_timertype_ticker:
140 		timer->timeout = timer->repeat = isc_interval_ms(interval);
141 		break;
142 	default:
143 		UNREACHABLE();
144 	}
145 
146 	atomic_store_release(&timer->running, true);
147 	r = uv_timer_start(&timer->timer, timer_cb, timer->timeout,
148 			   timer->repeat);
149 	UV_RUNTIME_CHECK(uv_timer_start, r);
150 }
151 
152 static void
153 timer_close(uv_handle_t *handle) {
154 	isc_timer_t *timer = uv_handle_get_data(handle);
155 	isc_loop_t *loop;
156 
157 	REQUIRE(VALID_TIMER(timer));
158 
159 	loop = timer->loop;
160 
161 	isc_mem_put(loop->mctx, timer, sizeof(*timer));
162 
163 	isc_loop_detach(&loop);
164 }
165 
166 static void
167 timer_destroy(void *arg) {
168 	isc_timer_t *timer = arg;
169 
170 	atomic_store_release(&timer->running, false);
171 	uv_timer_stop(&timer->timer);
172 	uv_close(&timer->timer, timer_close);
173 }
174 
175 void
176 isc_timer_destroy(isc_timer_t **timerp) {
177 	isc_timer_t *timer = NULL;
178 
179 	REQUIRE(timerp != NULL && VALID_TIMER(*timerp));
180 
181 	timer = *timerp;
182 	*timerp = NULL;
183 
184 	REQUIRE(timer->loop == isc_loop());
185 
186 	timer_destroy(timer);
187 }
188 
189 void
190 isc_timer_async_destroy(isc_timer_t **timerp) {
191 	isc_timer_t *timer = NULL;
192 
193 	REQUIRE(timerp != NULL && VALID_TIMER(*timerp));
194 
195 	timer = *timerp;
196 	*timerp = NULL;
197 
198 	isc_timer_stop(timer);
199 	isc_async_run(timer->loop, timer_destroy, timer);
200 }
201