-
Notifications
You must be signed in to change notification settings - Fork 0
/
timeout.c
116 lines (102 loc) · 3.81 KB
/
timeout.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
* Copyright (c) 2014 DeNA Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include "h2o.h"
static inline int timer_is_empty(h2o_timeout_t *timer)
{
return timer->_link._prev == &timer->_link;
}
static void unlink_timer(h2o_timeout_entry_t *entry)
{
assert(entry->_prev != NULL);
entry->_prev->_next = entry->_next;
entry->_next->_prev = entry->_prev;
entry->_prev = NULL;
entry->_next = NULL;
entry->wake_at = 0;
}
static void timer_cb(uv_timer_t *_timer, int status)
{
h2o_timeout_t *timer = (void*)_timer;
h2o_timeout_entry_t detached_root;
uint64_t now;
if (status != 0) {
return;
}
if (timer_is_empty(timer)) {
return;
}
/* For zero timeout, we should only invoke the timers that were added prior to entering this function.
* To fullfill the purpose we detach all the linked entries from timer->_link and then iterate them.
*/
detached_root = timer->_link;
detached_root._next->_prev = &detached_root;
detached_root._prev->_next = &detached_root;
timer->_link._prev = timer->_link._next = &timer->_link;
now = uv_now(timer->timer.loop);
while (1) {
h2o_timeout_entry_t *entry = detached_root._next;
if (entry->wake_at > now) {
break;
}
unlink_timer(entry);
entry->cb(entry);
}
if (! timer_is_empty(timer)) {
/* not empty, schedule next timer */
uv_timer_start(&timer->timer, timer_cb, timer->_link._next->wake_at - now, 0);
}
}
void h2o_timeout_init(h2o_timeout_t *timer, uint64_t timeout, uv_loop_t *loop)
{
uv_timer_init(loop, &timer->timer);
uv_unref((uv_handle_t*)&timer->timer);
timer->timeout = timeout;
timer->_link.wake_at = UINT64_MAX;
timer->_link._prev = timer->_link._next = &timer->_link;
}
void h2o_timeout_link_entry(h2o_timeout_t *timer, h2o_timeout_entry_t *entry)
{
int was_empty = timer_is_empty(timer);
assert(entry->_prev == NULL);
/* set data */
entry->wake_at = uv_now(timer->timer.loop) + timer->timeout;
/* insert at tail, so the entries are sorted in ascending order */
entry->_prev = timer->_link._prev;
entry->_next = &timer->_link;
entry->_prev->_next = entry;
entry->_next->_prev = entry;
if (was_empty) {
uv_timer_start(&timer->timer, timer_cb, timer->timeout, 0);
}
}
void h2o_timeout_unlink_entry(h2o_timeout_t *timer, h2o_timeout_entry_t *entry)
{
if (entry->_prev != NULL) {
unlink_timer(entry);
/* note: timer is left scheduled even if no more entries are in linked to the list;
* because we think having timer fired every `timer->timeout` seconds at worst
* case is better than calling uv_timer_start / uv_timer_stop many many times
* for every loop
*/
}
}