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