/src/bind9/lib/isc/timer.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | /*! \file */ |
15 | | |
16 | | #include <stdbool.h> |
17 | | |
18 | | #include <isc/async.h> |
19 | | #include <isc/atomic.h> |
20 | | #include <isc/heap.h> |
21 | | #include <isc/job.h> |
22 | | #include <isc/log.h> |
23 | | #include <isc/magic.h> |
24 | | #include <isc/mem.h> |
25 | | #include <isc/once.h> |
26 | | #include <isc/refcount.h> |
27 | | #include <isc/thread.h> |
28 | | #include <isc/time.h> |
29 | | #include <isc/timer.h> |
30 | | #include <isc/util.h> |
31 | | #include <isc/uv.h> |
32 | | |
33 | | #include "loop_p.h" |
34 | | |
35 | 0 | #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R') |
36 | | #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC) |
37 | | |
38 | | struct isc_timer { |
39 | | unsigned int magic; |
40 | | isc_loop_t *loop; |
41 | | uv_timer_t timer; |
42 | | isc_job_cb cb; |
43 | | void *cbarg; |
44 | | uint64_t timeout; |
45 | | uint64_t repeat; |
46 | | atomic_bool running; |
47 | | }; |
48 | | |
49 | | void |
50 | | isc_timer_create(isc_loop_t *loop, isc_job_cb cb, void *cbarg, |
51 | 0 | isc_timer_t **timerp) { |
52 | 0 | int r; |
53 | 0 | isc_timer_t *timer; |
54 | |
|
55 | 0 | REQUIRE(cb != NULL); |
56 | 0 | REQUIRE(timerp != NULL && *timerp == NULL); |
57 | |
|
58 | 0 | REQUIRE(VALID_LOOP(loop)); |
59 | 0 | REQUIRE(loop == isc_loop()); |
60 | |
|
61 | 0 | timer = isc_mem_get(loop->mctx, sizeof(*timer)); |
62 | 0 | *timer = (isc_timer_t){ |
63 | 0 | .cb = cb, |
64 | 0 | .cbarg = cbarg, |
65 | 0 | .magic = TIMER_MAGIC, |
66 | 0 | }; |
67 | |
|
68 | 0 | isc_loop_attach(loop, &timer->loop); |
69 | |
|
70 | 0 | r = uv_timer_init(&loop->loop, &timer->timer); |
71 | 0 | UV_RUNTIME_CHECK(uv_timer_init, r); |
72 | 0 | uv_handle_set_data(&timer->timer, timer); |
73 | |
|
74 | 0 | *timerp = timer; |
75 | 0 | } |
76 | | |
77 | | void |
78 | 0 | isc_timer_stop(isc_timer_t *timer) { |
79 | 0 | REQUIRE(VALID_TIMER(timer)); |
80 | |
|
81 | 0 | if (!atomic_compare_exchange_strong_acq_rel(&timer->running, |
82 | 0 | &(bool){ true }, false)) |
83 | 0 | { |
84 | | /* Timer was already stopped */ |
85 | 0 | return; |
86 | 0 | } |
87 | | |
88 | | /* Stop the timer, if the loops are matching */ |
89 | 0 | if (timer->loop == isc_loop()) { |
90 | 0 | uv_timer_stop(&timer->timer); |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | static void |
95 | | timer_cb(uv_timer_t *handle) { |
96 | | isc_timer_t *timer = uv_handle_get_data(handle); |
97 | | |
98 | | REQUIRE(VALID_TIMER(timer)); |
99 | | |
100 | | if (!atomic_load_acquire(&timer->running)) { |
101 | | uv_timer_stop(&timer->timer); |
102 | | return; |
103 | | } |
104 | | |
105 | | timer->cb(timer->cbarg); |
106 | | } |
107 | | |
108 | | void |
109 | | isc_timer_start(isc_timer_t *timer, isc_timertype_t type, |
110 | 0 | const isc_interval_t *interval) { |
111 | 0 | isc_loop_t *loop = NULL; |
112 | 0 | int r; |
113 | |
|
114 | 0 | REQUIRE(VALID_TIMER(timer)); |
115 | 0 | REQUIRE(type == isc_timertype_ticker || type == isc_timertype_once); |
116 | 0 | REQUIRE(timer->loop == isc_loop()); |
117 | |
|
118 | 0 | loop = timer->loop; |
119 | |
|
120 | 0 | REQUIRE(VALID_LOOP(loop)); |
121 | |
|
122 | 0 | switch (type) { |
123 | 0 | case isc_timertype_once: |
124 | 0 | timer->timeout = isc_interval_ms(interval); |
125 | 0 | timer->repeat = 0; |
126 | 0 | break; |
127 | 0 | case isc_timertype_ticker: |
128 | 0 | timer->timeout = timer->repeat = isc_interval_ms(interval); |
129 | 0 | break; |
130 | 0 | default: |
131 | 0 | UNREACHABLE(); |
132 | 0 | } |
133 | | |
134 | 0 | atomic_store_release(&timer->running, true); |
135 | 0 | r = uv_timer_start(&timer->timer, timer_cb, timer->timeout, |
136 | 0 | timer->repeat); |
137 | 0 | UV_RUNTIME_CHECK(uv_timer_start, r); |
138 | 0 | } |
139 | | |
140 | | static void |
141 | 0 | timer_close(uv_handle_t *handle) { |
142 | 0 | isc_timer_t *timer = uv_handle_get_data(handle); |
143 | 0 | isc_loop_t *loop; |
144 | |
|
145 | 0 | REQUIRE(VALID_TIMER(timer)); |
146 | |
|
147 | 0 | loop = timer->loop; |
148 | |
|
149 | 0 | isc_mem_put(loop->mctx, timer, sizeof(*timer)); |
150 | |
|
151 | 0 | isc_loop_detach(&loop); |
152 | 0 | } |
153 | | |
154 | | static void |
155 | | timer_destroy(void *arg) { |
156 | | isc_timer_t *timer = arg; |
157 | | |
158 | | atomic_store_release(&timer->running, false); |
159 | | uv_timer_stop(&timer->timer); |
160 | | uv_close(&timer->timer, timer_close); |
161 | | } |
162 | | |
163 | | void |
164 | 0 | isc_timer_destroy(isc_timer_t **timerp) { |
165 | 0 | isc_timer_t *timer = NULL; |
166 | |
|
167 | 0 | REQUIRE(timerp != NULL && VALID_TIMER(*timerp)); |
168 | |
|
169 | 0 | timer = *timerp; |
170 | 0 | *timerp = NULL; |
171 | |
|
172 | 0 | REQUIRE(timer->loop == isc_loop()); |
173 | |
|
174 | 0 | timer_destroy(timer); |
175 | 0 | } |
176 | | |
177 | | void |
178 | 0 | isc_timer_async_destroy(isc_timer_t **timerp) { |
179 | 0 | isc_timer_t *timer = NULL; |
180 | |
|
181 | 0 | REQUIRE(timerp != NULL && VALID_TIMER(*timerp)); |
182 | |
|
183 | 0 | timer = *timerp; |
184 | 0 | *timerp = NULL; |
185 | |
|
186 | 0 | isc_timer_stop(timer); |
187 | 0 | isc_async_run(timer->loop, timer_destroy, timer); |
188 | 0 | } |
189 | | |
190 | | bool |
191 | 0 | isc_timer_running(isc_timer_t *timer) { |
192 | 0 | REQUIRE(VALID_TIMER(timer)); |
193 | |
|
194 | | return atomic_load_acquire(&timer->running); |
195 | 0 | } |