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