Home | History | Annotate | Line # | Download | only in isc
timer.c revision 1.1.1.12
      1 /*	$NetBSD: timer.c,v 1.1.1.12 2025/01/26 16:12:30 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