1 /* $NetBSD: timer.c,v 1.16 2025/05/21 14:48:05 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/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