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