Home | History | Annotate | Line # | Download | only in isc
      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