timer.c revision 1.1.1.12 1 /* $NetBSD: timer.c,v 1.1.1.12 2025/01/26 16:12:30 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