timer.c revision 1.13.2.1 1 /* $NetBSD: timer.c,v 1.13.2.1 2025/08/02 05:53:56 perseant 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/atomic.h>
22 #include <isc/condition.h>
23 #include <isc/heap.h>
24 #include <isc/job.h>
25 #include <isc/log.h>
26 #include <isc/magic.h>
27 #include <isc/mem.h>
28 #include <isc/once.h>
29 #include <isc/refcount.h>
30 #include <isc/thread.h>
31 #include <isc/time.h>
32 #include <isc/timer.h>
33 #include <isc/util.h>
34 #include <isc/uv.h>
35
36 #include "loop_p.h"
37
38 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
39 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
40
41 struct isc_timer {
42 unsigned int magic;
43 isc_loop_t *loop;
44 uv_timer_t timer;
45 isc_job_cb cb;
46 void *cbarg;
47 uint64_t timeout;
48 uint64_t repeat;
49 atomic_bool running;
50 };
51
52 void
53 isc_timer_create(isc_loop_t *loop, isc_job_cb cb, void *cbarg,
54 isc_timer_t **timerp) {
55 int r;
56 isc_timer_t *timer;
57 isc_loopmgr_t *loopmgr = NULL;
58
59 REQUIRE(cb != NULL);
60 REQUIRE(timerp != NULL && *timerp == NULL);
61
62 REQUIRE(VALID_LOOP(loop));
63
64 loopmgr = loop->loopmgr;
65
66 REQUIRE(VALID_LOOPMGR(loopmgr));
67 REQUIRE(loop == isc_loop());
68
69 timer = isc_mem_get(loop->mctx, sizeof(*timer));
70 *timer = (isc_timer_t){
71 .cb = cb,
72 .cbarg = cbarg,
73 .magic = TIMER_MAGIC,
74 };
75
76 isc_loop_attach(loop, &timer->loop);
77
78 r = uv_timer_init(&loop->loop, &timer->timer);
79 UV_RUNTIME_CHECK(uv_timer_init, r);
80 uv_handle_set_data(&timer->timer, timer);
81
82 *timerp = timer;
83 }
84
85 void
86 isc_timer_stop(isc_timer_t *timer) {
87 REQUIRE(VALID_TIMER(timer));
88
89 if (!atomic_compare_exchange_strong_acq_rel(&timer->running,
90 &(bool){ true }, false))
91 {
92 /* Timer was already stopped */
93 return;
94 }
95
96 /* Stop the timer, if the loops are matching */
97 if (timer->loop == isc_loop()) {
98 uv_timer_stop(&timer->timer);
99 }
100 }
101
102 static void
103 timer_cb(uv_timer_t *handle) {
104 isc_timer_t *timer = uv_handle_get_data(handle);
105
106 REQUIRE(VALID_TIMER(timer));
107
108 if (!atomic_load_acquire(&timer->running)) {
109 uv_timer_stop(&timer->timer);
110 return;
111 }
112
113 timer->cb(timer->cbarg);
114 }
115
116 void
117 isc_timer_start(isc_timer_t *timer, isc_timertype_t type,
118 const isc_interval_t *interval) {
119 isc_loopmgr_t *loopmgr = NULL;
120 isc_loop_t *loop = NULL;
121 int r;
122
123 REQUIRE(VALID_TIMER(timer));
124 REQUIRE(type == isc_timertype_ticker || type == isc_timertype_once);
125 REQUIRE(timer->loop == isc_loop());
126
127 loop = timer->loop;
128
129 REQUIRE(VALID_LOOP(loop));
130
131 loopmgr = loop->loopmgr;
132
133 REQUIRE(VALID_LOOPMGR(loopmgr));
134
135 switch (type) {
136 case isc_timertype_once:
137 timer->timeout = isc_interval_ms(interval);
138 timer->repeat = 0;
139 break;
140 case isc_timertype_ticker:
141 timer->timeout = timer->repeat = isc_interval_ms(interval);
142 break;
143 default:
144 UNREACHABLE();
145 }
146
147 atomic_store_release(&timer->running, true);
148 r = uv_timer_start(&timer->timer, timer_cb, timer->timeout,
149 timer->repeat);
150 UV_RUNTIME_CHECK(uv_timer_start, r);
151 }
152
153 static void
154 timer_close(uv_handle_t *handle) {
155 isc_timer_t *timer = uv_handle_get_data(handle);
156 isc_loop_t *loop;
157
158 REQUIRE(VALID_TIMER(timer));
159
160 loop = timer->loop;
161
162 isc_mem_put(loop->mctx, timer, sizeof(*timer));
163
164 isc_loop_detach(&loop);
165 }
166
167 static void
168 timer_destroy(void *arg) {
169 isc_timer_t *timer = arg;
170
171 atomic_store_release(&timer->running, false);
172 uv_timer_stop(&timer->timer);
173 uv_close(&timer->timer, timer_close);
174 }
175
176 void
177 isc_timer_destroy(isc_timer_t **timerp) {
178 isc_timer_t *timer = NULL;
179
180 REQUIRE(timerp != NULL && VALID_TIMER(*timerp));
181
182 timer = *timerp;
183 *timerp = NULL;
184
185 REQUIRE(timer->loop == isc_loop());
186
187 timer_destroy(timer);
188 }
189
190 void
191 isc_timer_async_destroy(isc_timer_t **timerp) {
192 isc_timer_t *timer = NULL;
193
194 REQUIRE(timerp != NULL && VALID_TIMER(*timerp));
195
196 timer = *timerp;
197 *timerp = NULL;
198
199 isc_timer_stop(timer);
200 isc_async_run(timer->loop, timer_destroy, timer);
201 }
202
203 bool
204 isc_timer_running(isc_timer_t *timer) {
205 REQUIRE(VALID_TIMER(timer));
206
207 return atomic_load_acquire(&timer->running);
208 }
209