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