/src/unit/src/nxt_timer.c
Line | Count | Source |
1 | | |
2 | | /* |
3 | | * Copyright (C) Igor Sysoev |
4 | | * Copyright (C) NGINX, Inc. |
5 | | */ |
6 | | |
7 | | #include <nxt_main.h> |
8 | | |
9 | | |
10 | | /* |
11 | | * Timer operations are batched in the changes array to improve instruction |
12 | | * and data cache locality of rbtree operations. |
13 | | * |
14 | | * nxt_timer_add() adds or modify a timer. |
15 | | * |
16 | | * nxt_timer_disable() disables a timer. |
17 | | * |
18 | | * nxt_timer_delete() deletes a timer. It returns 1 if there are pending |
19 | | * changes in the changes array or 0 otherwise. |
20 | | */ |
21 | | |
22 | | static intptr_t nxt_timer_rbtree_compare(nxt_rbtree_node_t *node1, |
23 | | nxt_rbtree_node_t *node2); |
24 | | static void nxt_timer_change(nxt_event_engine_t *engine, nxt_timer_t *timer, |
25 | | nxt_timer_operation_t change, nxt_msec_t time); |
26 | | static void nxt_timer_changes_commit(nxt_event_engine_t *engine); |
27 | | static void nxt_timer_handler(nxt_task_t *task, void *obj, void *data); |
28 | | |
29 | | |
30 | | nxt_int_t |
31 | | nxt_timers_init(nxt_timers_t *timers, nxt_uint_t mchanges) |
32 | 0 | { |
33 | 0 | nxt_rbtree_init(&timers->tree, nxt_timer_rbtree_compare); |
34 | |
|
35 | 0 | if (mchanges > NXT_TIMER_MAX_CHANGES) { |
36 | 0 | mchanges = NXT_TIMER_MAX_CHANGES; |
37 | 0 | } |
38 | |
|
39 | 0 | timers->mchanges = mchanges; |
40 | |
|
41 | 0 | timers->changes = nxt_malloc(sizeof(nxt_timer_change_t) * mchanges); |
42 | |
|
43 | 0 | if (nxt_fast_path(timers->changes != NULL)) { |
44 | 0 | return NXT_OK; |
45 | 0 | } |
46 | | |
47 | 0 | return NXT_ERROR; |
48 | 0 | } |
49 | | |
50 | | |
51 | | static intptr_t |
52 | | nxt_timer_rbtree_compare(nxt_rbtree_node_t *node1, nxt_rbtree_node_t *node2) |
53 | 0 | { |
54 | 0 | nxt_timer_t *timer1, *timer2; |
55 | |
|
56 | 0 | timer1 = (nxt_timer_t *) node1; |
57 | 0 | timer2 = (nxt_timer_t *) node2; |
58 | | |
59 | | /* |
60 | | * Timer values are distributed in small range, usually several minutes |
61 | | * and overflow every 49 days if nxt_msec_t is stored in 32 bits. |
62 | | * This signed comparison takes into account that overflow. |
63 | | */ |
64 | | /* timer1->time < timer2->time */ |
65 | 0 | return nxt_msec_diff(timer1->time , timer2->time); |
66 | 0 | } |
67 | | |
68 | | |
69 | | void |
70 | | nxt_timer_add(nxt_event_engine_t *engine, nxt_timer_t *timer, |
71 | | nxt_msec_t timeout) |
72 | 0 | { |
73 | 0 | int32_t diff; |
74 | 0 | uint32_t time; |
75 | |
|
76 | 0 | time = engine->timers.now + timeout; |
77 | |
|
78 | 0 | nxt_debug(timer->task, "timer add: %M±%d %M:%M", |
79 | 0 | timer->time, timer->bias, timeout, time); |
80 | |
|
81 | 0 | timer->enabled = 1; |
82 | |
|
83 | 0 | if (nxt_timer_is_in_tree(timer)) { |
84 | |
|
85 | 0 | diff = nxt_msec_diff(time, timer->time); |
86 | | /* |
87 | | * Use the previous timer if difference between it and the |
88 | | * new timer is within bias: this decreases number of rbtree |
89 | | * operations for fast connections. |
90 | | */ |
91 | 0 | if (nxt_abs(diff) <= timer->bias) { |
92 | 0 | nxt_debug(timer->task, "timer previous: %M±%d", |
93 | 0 | time, timer->bias); |
94 | |
|
95 | 0 | nxt_timer_change(engine, timer, NXT_TIMER_NOPE, 0); |
96 | 0 | return; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | nxt_timer_change(engine, timer, NXT_TIMER_ADD, time); |
101 | 0 | } |
102 | | |
103 | | |
104 | | nxt_bool_t |
105 | | nxt_timer_delete(nxt_event_engine_t *engine, nxt_timer_t *timer) |
106 | 0 | { |
107 | 0 | nxt_debug(timer->task, "timer delete: %M±%d", |
108 | 0 | timer->time, timer->bias); |
109 | |
|
110 | 0 | timer->enabled = 0; |
111 | |
|
112 | 0 | if (nxt_timer_is_in_tree(timer)) { |
113 | |
|
114 | 0 | nxt_timer_change(engine, timer, NXT_TIMER_DELETE, 0); |
115 | |
|
116 | 0 | return 1; |
117 | 0 | } |
118 | | |
119 | 0 | nxt_timer_change(engine, timer, NXT_TIMER_NOPE, 0); |
120 | |
|
121 | 0 | return (timer->queued || timer->change != NXT_TIMER_NO_CHANGE); |
122 | 0 | } |
123 | | |
124 | | |
125 | | static void |
126 | | nxt_timer_change(nxt_event_engine_t *engine, nxt_timer_t *timer, |
127 | | nxt_timer_operation_t change, nxt_msec_t time) |
128 | 0 | { |
129 | 0 | nxt_timers_t *timers; |
130 | 0 | nxt_timer_change_t *ch; |
131 | |
|
132 | 0 | timers = &engine->timers; |
133 | |
|
134 | 0 | if (timer->change == NXT_TIMER_NO_CHANGE) { |
135 | |
|
136 | 0 | if (change == NXT_TIMER_NOPE) { |
137 | 0 | return; |
138 | 0 | } |
139 | | |
140 | 0 | if (timers->nchanges >= timers->mchanges) { |
141 | 0 | nxt_timer_changes_commit(engine); |
142 | 0 | } |
143 | |
|
144 | 0 | timers->nchanges++; |
145 | 0 | timer->change = timers->nchanges; |
146 | 0 | } |
147 | | |
148 | 0 | nxt_debug(timer->task, "timer change: %M±%d:%d", |
149 | 0 | time, timer->bias, change); |
150 | |
|
151 | 0 | ch = &timers->changes[timer->change - 1]; |
152 | |
|
153 | 0 | ch->change = change; |
154 | 0 | ch->time = time; |
155 | 0 | ch->timer = timer; |
156 | 0 | } |
157 | | |
158 | | |
159 | | static void |
160 | | nxt_timer_changes_commit(nxt_event_engine_t *engine) |
161 | 0 | { |
162 | 0 | nxt_timer_t *timer; |
163 | 0 | nxt_timers_t *timers; |
164 | 0 | nxt_timer_change_t *ch, *end, *add, *add_end; |
165 | |
|
166 | 0 | timers = &engine->timers; |
167 | |
|
168 | 0 | nxt_debug(&engine->task, "timers changes: %ui", timers->nchanges); |
169 | |
|
170 | 0 | ch = timers->changes; |
171 | 0 | end = ch + timers->nchanges; |
172 | |
|
173 | 0 | add = ch; |
174 | 0 | add_end = add; |
175 | |
|
176 | 0 | while (ch < end) { |
177 | 0 | timer = ch->timer; |
178 | |
|
179 | 0 | switch (ch->change) { |
180 | | |
181 | 0 | case NXT_TIMER_NOPE: |
182 | 0 | break; |
183 | | |
184 | 0 | case NXT_TIMER_ADD: |
185 | |
|
186 | 0 | timer->time = ch->time; |
187 | |
|
188 | 0 | add_end->timer = timer; |
189 | 0 | add_end++; |
190 | |
|
191 | 0 | if (!nxt_timer_is_in_tree(timer)) { |
192 | 0 | break; |
193 | 0 | } |
194 | | |
195 | | /* Fall through. */ |
196 | | |
197 | 0 | case NXT_TIMER_DELETE: |
198 | 0 | nxt_debug(timer->task, "timer rbtree delete: %M±%d", |
199 | 0 | timer->time, timer->bias); |
200 | |
|
201 | 0 | nxt_rbtree_delete(&timers->tree, &timer->node); |
202 | 0 | nxt_timer_in_tree_clear(timer); |
203 | |
|
204 | 0 | break; |
205 | 0 | } |
206 | | |
207 | 0 | timer->change = NXT_TIMER_NO_CHANGE; |
208 | |
|
209 | 0 | ch++; |
210 | 0 | } |
211 | | |
212 | 0 | while (add < add_end) { |
213 | 0 | timer = add->timer; |
214 | |
|
215 | 0 | nxt_debug(timer->task, "timer rbtree insert: %M±%d", |
216 | 0 | timer->time, timer->bias); |
217 | |
|
218 | 0 | nxt_rbtree_insert(&timers->tree, &timer->node); |
219 | 0 | nxt_timer_in_tree_set(timer); |
220 | |
|
221 | 0 | add++; |
222 | 0 | } |
223 | |
|
224 | 0 | timers->nchanges = 0; |
225 | 0 | } |
226 | | |
227 | | |
228 | | nxt_msec_t |
229 | | nxt_timer_find(nxt_event_engine_t *engine) |
230 | 0 | { |
231 | 0 | int32_t delta; |
232 | 0 | nxt_msec_t time; |
233 | 0 | nxt_timer_t *timer; |
234 | 0 | nxt_timers_t *timers; |
235 | 0 | nxt_rbtree_t *tree; |
236 | 0 | nxt_rbtree_node_t *node, *next; |
237 | |
|
238 | 0 | timers = &engine->timers; |
239 | |
|
240 | 0 | if (timers->nchanges != 0) { |
241 | 0 | nxt_timer_changes_commit(engine); |
242 | 0 | } |
243 | |
|
244 | 0 | tree = &timers->tree; |
245 | |
|
246 | 0 | for (node = nxt_rbtree_min(tree); |
247 | 0 | nxt_rbtree_is_there_successor(tree, node); |
248 | 0 | node = next) |
249 | 0 | { |
250 | 0 | next = nxt_rbtree_node_successor(tree, node); |
251 | |
|
252 | 0 | timer = (nxt_timer_t *) node; |
253 | | |
254 | | /* |
255 | | * Disabled timers are not deleted here since the minimum active |
256 | | * timer may be larger than a disabled timer, but event poll may |
257 | | * return much earlier and the disabled timer can be reactivated. |
258 | | */ |
259 | |
|
260 | 0 | if (timer->enabled) { |
261 | 0 | time = timer->time; |
262 | 0 | timers->minimum = time - timer->bias; |
263 | |
|
264 | 0 | nxt_debug(timer->task, "timer found minimum: %M±%d:%M", |
265 | 0 | time, timer->bias, timers->now); |
266 | |
|
267 | 0 | delta = nxt_msec_diff(time, timers->now); |
268 | |
|
269 | 0 | return (nxt_msec_t) nxt_max(delta, 0); |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | | /* Set minimum time one day ahead. */ |
274 | 0 | timers->minimum = timers->now + 24 * 60 * 60 * 1000; |
275 | |
|
276 | 0 | return NXT_INFINITE_MSEC; |
277 | 0 | } |
278 | | |
279 | | |
280 | | void |
281 | | nxt_timer_expire(nxt_event_engine_t *engine, nxt_msec_t now) |
282 | 0 | { |
283 | 0 | nxt_timer_t *timer; |
284 | 0 | nxt_timers_t *timers; |
285 | 0 | nxt_rbtree_t *tree; |
286 | 0 | nxt_rbtree_node_t *node, *next; |
287 | |
|
288 | 0 | timers = &engine->timers; |
289 | 0 | timers->now = now; |
290 | |
|
291 | 0 | nxt_debug(&engine->task, "timer expire minimum: %M:%M", |
292 | 0 | timers->minimum, now); |
293 | | |
294 | | /* timers->minimum > now */ |
295 | 0 | if (nxt_msec_diff(timers->minimum , now) > 0) { |
296 | 0 | return; |
297 | 0 | } |
298 | | |
299 | 0 | tree = &timers->tree; |
300 | |
|
301 | 0 | for (node = nxt_rbtree_min(tree); |
302 | 0 | nxt_rbtree_is_there_successor(tree, node); |
303 | 0 | node = next) |
304 | 0 | { |
305 | 0 | timer = (nxt_timer_t *) node; |
306 | | |
307 | | /* timer->time > now + timer->bias */ |
308 | 0 | if (nxt_msec_diff(timer->time , now) > (int32_t) timer->bias) { |
309 | 0 | return; |
310 | 0 | } |
311 | | |
312 | 0 | next = nxt_rbtree_node_successor(tree, node); |
313 | |
|
314 | 0 | nxt_debug(timer->task, "timer expire delete: %M±%d", |
315 | 0 | timer->time, timer->bias); |
316 | |
|
317 | 0 | nxt_rbtree_delete(tree, &timer->node); |
318 | 0 | nxt_timer_in_tree_clear(timer); |
319 | |
|
320 | 0 | if (timer->enabled) { |
321 | 0 | timer->queued = 1; |
322 | |
|
323 | 0 | nxt_work_queue_add(timer->work_queue, nxt_timer_handler, |
324 | 0 | timer->task, timer, NULL); |
325 | 0 | } |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | |
330 | | static void |
331 | | nxt_timer_handler(nxt_task_t *task, void *obj, void *data) |
332 | 0 | { |
333 | 0 | nxt_timer_t *timer; |
334 | |
|
335 | 0 | timer = obj; |
336 | |
|
337 | 0 | timer->queued = 0; |
338 | |
|
339 | 0 | if (timer->enabled && timer->change == NXT_TIMER_NO_CHANGE) { |
340 | 0 | timer->enabled = 0; |
341 | |
|
342 | | timer->handler(task, timer, NULL); |
343 | 0 | } |
344 | 0 | } |