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