timeout_ext 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/COPYING +510 -0
- data/MANIFEST +51 -0
- data/README +15 -0
- data/Rakefile +28 -0
- data/ext/timeout_ext/.gitignore +3 -0
- data/ext/timeout_ext/ccan-bits.c +7 -0
- data/ext/timeout_ext/ccan/array_size/LICENSE +28 -0
- data/ext/timeout_ext/ccan/array_size/_info +46 -0
- data/ext/timeout_ext/ccan/array_size/array_size.h +26 -0
- data/ext/timeout_ext/ccan/build_assert/LICENSE +28 -0
- data/ext/timeout_ext/ccan/build_assert/_info +49 -0
- data/ext/timeout_ext/ccan/build_assert/build_assert.h +40 -0
- data/ext/timeout_ext/ccan/check_type/LICENSE +28 -0
- data/ext/timeout_ext/ccan/check_type/_info +33 -0
- data/ext/timeout_ext/ccan/check_type/check_type.h +64 -0
- data/ext/timeout_ext/ccan/compiler/LICENSE +28 -0
- data/ext/timeout_ext/ccan/compiler/_info +64 -0
- data/ext/timeout_ext/ccan/compiler/compiler.h +231 -0
- data/ext/timeout_ext/ccan/container_of/LICENSE +28 -0
- data/ext/timeout_ext/ccan/container_of/_info +65 -0
- data/ext/timeout_ext/ccan/container_of/container_of.h +145 -0
- data/ext/timeout_ext/ccan/ilog/LICENSE +28 -0
- data/ext/timeout_ext/ccan/ilog/_info +50 -0
- data/ext/timeout_ext/ccan/ilog/ilog.c +141 -0
- data/ext/timeout_ext/ccan/ilog/ilog.h +151 -0
- data/ext/timeout_ext/ccan/list/LICENSE +17 -0
- data/ext/timeout_ext/ccan/list/_info +72 -0
- data/ext/timeout_ext/ccan/list/list.h +842 -0
- data/ext/timeout_ext/ccan/str/LICENSE +28 -0
- data/ext/timeout_ext/ccan/str/_info +52 -0
- data/ext/timeout_ext/ccan/str/str.h +228 -0
- data/ext/timeout_ext/ccan/str/str_debug.h +30 -0
- data/ext/timeout_ext/ccan/time/LICENSE +17 -0
- data/ext/timeout_ext/ccan/time/_info +57 -0
- data/ext/timeout_ext/ccan/time/time.c +138 -0
- data/ext/timeout_ext/ccan/time/time.h +753 -0
- data/ext/timeout_ext/ccan/timer/LICENSE +510 -0
- data/ext/timeout_ext/ccan/timer/_info +79 -0
- data/ext/timeout_ext/ccan/timer/design.txt +76 -0
- data/ext/timeout_ext/ccan/timer/timer.c +524 -0
- data/ext/timeout_ext/ccan/timer/timer.h +211 -0
- data/ext/timeout_ext/depend +17 -0
- data/ext/timeout_ext/extconf.rb +50 -0
- data/ext/timeout_ext/licenses/BSD-MIT +17 -0
- data/ext/timeout_ext/licenses/CC0 +28 -0
- data/ext/timeout_ext/licenses/LGPL-2.1 +510 -0
- data/ext/timeout_ext/missing/stdbool/stdbool.h +20 -0
- data/ext/timeout_ext/timeout_ext.c +114 -0
- data/test/test_timeout_ext.rb +44 -0
- data/timeout_ext.gemspec +30 -0
- metadata +126 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
#include "config.h"
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <string.h>
|
4
|
+
|
5
|
+
/**
|
6
|
+
* timer - efficient implementation of rarely-expiring timers.
|
7
|
+
*
|
8
|
+
* This is a lazy implementation of timers: you can add and delete timers
|
9
|
+
* very quickly, and they are only sorted as their expiry approaches.
|
10
|
+
*
|
11
|
+
* This is a common case for timeouts, which must often be set, but
|
12
|
+
* rarely expire.
|
13
|
+
*
|
14
|
+
* Example:
|
15
|
+
* // Silly example which outputs strings until timers expire.
|
16
|
+
* #include <ccan/timer/timer.h>
|
17
|
+
* #include <ccan/time/time.h>
|
18
|
+
* #include <stdlib.h>
|
19
|
+
* #include <stdio.h>
|
20
|
+
*
|
21
|
+
* struct timed_string {
|
22
|
+
* struct list_node node;
|
23
|
+
* struct timer timer;
|
24
|
+
* const char *string;
|
25
|
+
* };
|
26
|
+
*
|
27
|
+
* int main(int argc, char *argv[])
|
28
|
+
* {
|
29
|
+
* struct timers timers;
|
30
|
+
* struct list_head strings;
|
31
|
+
* struct timer *t;
|
32
|
+
* struct timed_string *s;
|
33
|
+
*
|
34
|
+
* (void)argc;
|
35
|
+
* timers_init(&timers, time_mono());
|
36
|
+
* list_head_init(&strings);
|
37
|
+
*
|
38
|
+
* while (argv[1]) {
|
39
|
+
* s = malloc(sizeof(*s));
|
40
|
+
* s->string = argv[1];
|
41
|
+
* timer_addrel(&timers, &s->timer,
|
42
|
+
* time_from_msec(atol(argv[2])));
|
43
|
+
* list_add_tail(&strings, &s->node);
|
44
|
+
* argv += 2;
|
45
|
+
* }
|
46
|
+
*
|
47
|
+
* while (!list_empty(&strings)) {
|
48
|
+
* struct timemono now = time_mono();
|
49
|
+
* list_for_each(&strings, s, node)
|
50
|
+
* printf("%s", s->string);
|
51
|
+
* while ((t = timers_expire(&timers, now)) != NULL) {
|
52
|
+
* s = container_of(t, struct timed_string, timer);
|
53
|
+
* list_del_from(&strings, &s->node);
|
54
|
+
* free(s);
|
55
|
+
* }
|
56
|
+
* }
|
57
|
+
*
|
58
|
+
* exit(0);
|
59
|
+
* }
|
60
|
+
*
|
61
|
+
* License: LGPL (v2.1 or any later version)
|
62
|
+
* Author: Rusty Russell <rusty@rustcorp.com.au>
|
63
|
+
*/
|
64
|
+
int main(int argc, char *argv[])
|
65
|
+
{
|
66
|
+
/* Expect exactly one argument */
|
67
|
+
if (argc != 2)
|
68
|
+
return 1;
|
69
|
+
|
70
|
+
if (strcmp(argv[1], "depends") == 0) {
|
71
|
+
printf("ccan/array_size\n");
|
72
|
+
printf("ccan/ilog\n");
|
73
|
+
printf("ccan/list\n");
|
74
|
+
printf("ccan/time\n");
|
75
|
+
return 0;
|
76
|
+
}
|
77
|
+
|
78
|
+
return 1;
|
79
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
Cascading timer design.
|
2
|
+
|
3
|
+
Inspired by the Linux kernel approach, documented roughly at:
|
4
|
+
https://lwn.net/Articles/152436/
|
5
|
+
|
6
|
+
For easy description, we use whole seconds and powers of 10: in the
|
7
|
+
implementation, we use powers of 2 (eg. 256 entries) and smaller
|
8
|
+
granularities.
|
9
|
+
|
10
|
+
We start with a simple data structure:
|
11
|
+
|
12
|
+
struct timer_level {
|
13
|
+
struct timer_level *next;
|
14
|
+
|
15
|
+
/* Ten buckets: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] */
|
16
|
+
struct list_head bucket[10];
|
17
|
+
};
|
18
|
+
|
19
|
+
struct timers {
|
20
|
+
/* We can never have a timer before this, aka "now". */
|
21
|
+
time_t offset;
|
22
|
+
|
23
|
+
struct timer_level *level;
|
24
|
+
|
25
|
+
/* Anything too far in the future. */
|
26
|
+
struct list_head far;
|
27
|
+
}
|
28
|
+
|
29
|
+
The first level of timers holds anything which will happen in the next
|
30
|
+
10 seconds. The next level holds things which will happen in the next
|
31
|
+
100 seconds. And so on.
|
32
|
+
|
33
|
+
When we want to add a new timer into the structure, we need to figure
|
34
|
+
out first what level it goes into, and second, which bucket. Say our
|
35
|
+
offset is 500,000,001 (about Tue Nov 5, 1985 in Unix time). And our
|
36
|
+
timer is set to go off in 5 seconds, ie. 500,000,006.
|
37
|
+
|
38
|
+
The level is easy: the difference between the timer and the offset is
|
39
|
+
5, and that's less than 10, so it's in the first level. The position,
|
40
|
+
however, depends on the absolute time, in this case the last digit 6,
|
41
|
+
so it's in bucket 6.
|
42
|
+
|
43
|
+
Adding a timer at 500,000,123? The difference is > 100 and < 1000, so
|
44
|
+
it's in the third level. The bucket is 1. If there's no third level,
|
45
|
+
we just add it to the 'far' list for stuff which is in the far future.
|
46
|
+
|
47
|
+
Deleting a timer is as simple as removing it; there is no external
|
48
|
+
bookkeeping in this scheme. This matters, since timers used for
|
49
|
+
timeouts are almost always deleted before they expire.
|
50
|
+
|
51
|
+
Now, when a second passes, we need to know if there are any timers
|
52
|
+
which are due. We increment the offset to 500,000,002, and look in
|
53
|
+
the first level, bucket 2 for any timers, so lookup is simple.
|
54
|
+
|
55
|
+
We do this eight more times, and we increment the offset to
|
56
|
+
500,000,010. We've swept around back to bucket 0, though it may not
|
57
|
+
be empty if we added more timers as we were going.
|
58
|
+
|
59
|
+
But we need to look into the next level since a timer at 500,000,010
|
60
|
+
added when the offset was 500,000,000 would have gone up there. We
|
61
|
+
empty bucket 1 (due to the '1' in 500,000,010) into these buckets,
|
62
|
+
which will contain timers between 500,000,010 and 500,000,019, which
|
63
|
+
all now are less than 10 seconds away, so belong in the bottom level.
|
64
|
+
|
65
|
+
Similarly, at 500,000,020 we will empty bucket 1 of the second level
|
66
|
+
into the first level. And at 500,000,100 we will empty bucket 1 of
|
67
|
+
the third level into the second level then bucket 0 of the second
|
68
|
+
level into the first level. We do it in this order, since emptying
|
69
|
+
bucket 1 on the third level (500,000,100 - 500,000,199) may put more
|
70
|
+
entries (500,000,100 - 500,000,109) into bucket 0 on the second level.
|
71
|
+
|
72
|
+
When we get to 500,001,000 we should empty the fourth level. If there
|
73
|
+
is no fourth level, that's when we sort through the 'far' list and
|
74
|
+
empty any which are less than 500,002,000. If there are many entries
|
75
|
+
in the far list, we should add more levels to reduce the number, or at
|
76
|
+
least the frequency we have to check it.
|
@@ -0,0 +1,524 @@
|
|
1
|
+
/* LGPL (v2.1 or any later version) - see LICENSE file for details */
|
2
|
+
#include <ccan/timer/timer.h>
|
3
|
+
#include <ccan/array_size/array_size.h>
|
4
|
+
#include <ccan/ilog/ilog.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <stdio.h>
|
7
|
+
|
8
|
+
#define PER_LEVEL (1ULL << TIMER_LEVEL_BITS)
|
9
|
+
|
10
|
+
struct timer_level {
|
11
|
+
struct list_head list[PER_LEVEL];
|
12
|
+
};
|
13
|
+
|
14
|
+
static uint64_t time_to_grains(struct timemono t)
|
15
|
+
{
|
16
|
+
return t.ts.tv_sec * ((uint64_t)1000000000 / TIMER_GRANULARITY)
|
17
|
+
+ (t.ts.tv_nsec / TIMER_GRANULARITY);
|
18
|
+
}
|
19
|
+
|
20
|
+
static struct timemono grains_to_time(uint64_t grains)
|
21
|
+
{
|
22
|
+
struct timemono t;
|
23
|
+
|
24
|
+
t.ts.tv_sec = grains / (1000000000 / TIMER_GRANULARITY);
|
25
|
+
t.ts.tv_nsec = (grains % (1000000000 / TIMER_GRANULARITY))
|
26
|
+
* TIMER_GRANULARITY;
|
27
|
+
return t;
|
28
|
+
}
|
29
|
+
|
30
|
+
void timers_init(struct timers *timers, struct timemono start)
|
31
|
+
{
|
32
|
+
unsigned int i;
|
33
|
+
|
34
|
+
list_head_init(&timers->far);
|
35
|
+
timers->base = time_to_grains(start);
|
36
|
+
timers->first = -1ULL;
|
37
|
+
memset(timers->firsts, 0xFF, sizeof(timers->firsts));
|
38
|
+
for (i = 0; i < ARRAY_SIZE(timers->level); i++)
|
39
|
+
timers->level[i] = NULL;
|
40
|
+
}
|
41
|
+
|
42
|
+
static unsigned int level_of(const struct timers *timers, uint64_t time)
|
43
|
+
{
|
44
|
+
uint64_t diff;
|
45
|
+
|
46
|
+
/* Level depends how far away it is. */
|
47
|
+
diff = time - timers->base;
|
48
|
+
return ilog64(diff / 2) / TIMER_LEVEL_BITS;
|
49
|
+
}
|
50
|
+
|
51
|
+
static void timer_add_raw(struct timers *timers, struct timer *t)
|
52
|
+
{
|
53
|
+
struct list_head *l;
|
54
|
+
unsigned int level = level_of(timers, t->time);
|
55
|
+
uint64_t *first;
|
56
|
+
|
57
|
+
if (!timers->level[level]) {
|
58
|
+
l = &timers->far;
|
59
|
+
first = &timers->firsts[ARRAY_SIZE(timers->level)];
|
60
|
+
} else {
|
61
|
+
int off = (t->time >> (level*TIMER_LEVEL_BITS)) & (PER_LEVEL-1);
|
62
|
+
l = &timers->level[level]->list[off];
|
63
|
+
first = &timers->firsts[level];
|
64
|
+
}
|
65
|
+
|
66
|
+
list_add_tail(l, &t->list);
|
67
|
+
if (t->time < *first)
|
68
|
+
*first = t->time;
|
69
|
+
}
|
70
|
+
|
71
|
+
void timer_init(struct timer *t)
|
72
|
+
{
|
73
|
+
list_node_init(&t->list);
|
74
|
+
}
|
75
|
+
|
76
|
+
static bool list_node_initted(const struct list_node *n)
|
77
|
+
{
|
78
|
+
return n->prev == n;
|
79
|
+
}
|
80
|
+
|
81
|
+
void timer_addrel(struct timers *timers, struct timer *t, struct timerel rel)
|
82
|
+
{
|
83
|
+
assert(list_node_initted(&t->list));
|
84
|
+
|
85
|
+
t->time = time_to_grains(timemono_add(time_mono(), rel));
|
86
|
+
|
87
|
+
#if TIME_HAVE_MONOTONIC
|
88
|
+
assert(t->time >= timers->base);
|
89
|
+
#else
|
90
|
+
/* Added in the past? Treat it as imminent. */
|
91
|
+
if (t->time < timers->base)
|
92
|
+
t->time = timers->base;
|
93
|
+
#endif
|
94
|
+
if (t->time < timers->first)
|
95
|
+
timers->first = t->time;
|
96
|
+
|
97
|
+
timer_add_raw(timers, t);
|
98
|
+
}
|
99
|
+
|
100
|
+
void timer_addmono(struct timers *timers, struct timer *t, struct timemono when)
|
101
|
+
{
|
102
|
+
assert(list_node_initted(&t->list));
|
103
|
+
|
104
|
+
t->time = time_to_grains(when);
|
105
|
+
|
106
|
+
/* Added in the past? Treat it as imminent. */
|
107
|
+
if (t->time < timers->base)
|
108
|
+
t->time = timers->base;
|
109
|
+
if (t->time < timers->first)
|
110
|
+
timers->first = t->time;
|
111
|
+
|
112
|
+
timer_add_raw(timers, t);
|
113
|
+
}
|
114
|
+
|
115
|
+
/* FIXME: inline */
|
116
|
+
void timer_del(struct timers *timers UNNEEDED, struct timer *t)
|
117
|
+
{
|
118
|
+
list_del_init(&t->list);
|
119
|
+
}
|
120
|
+
|
121
|
+
static void timers_far_get(struct timers *timers,
|
122
|
+
struct list_head *list,
|
123
|
+
uint64_t when)
|
124
|
+
{
|
125
|
+
struct timer *i, *next;
|
126
|
+
|
127
|
+
list_for_each_safe(&timers->far, i, next, list) {
|
128
|
+
if (i->time <= when) {
|
129
|
+
list_del_from(&timers->far, &i->list);
|
130
|
+
list_add_tail(list, &i->list);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
static void add_level(struct timers *timers, unsigned int level)
|
136
|
+
{
|
137
|
+
struct timer_level *l;
|
138
|
+
struct timer *t;
|
139
|
+
unsigned int i;
|
140
|
+
struct list_head from_far;
|
141
|
+
|
142
|
+
l = malloc(sizeof(*l));
|
143
|
+
if (!l)
|
144
|
+
return;
|
145
|
+
|
146
|
+
for (i = 0; i < ARRAY_SIZE(l->list); i++)
|
147
|
+
list_head_init(&l->list[i]);
|
148
|
+
timers->level[level] = l;
|
149
|
+
|
150
|
+
list_head_init(&from_far);
|
151
|
+
timers_far_get(timers, &from_far,
|
152
|
+
timers->base + (1ULL << ((level+1)*TIMER_LEVEL_BITS)) - 1);
|
153
|
+
|
154
|
+
while ((t = list_pop(&from_far, struct timer, list)) != NULL)
|
155
|
+
timer_add_raw(timers, t);
|
156
|
+
}
|
157
|
+
|
158
|
+
/* We don't need to search past the first at level 0, since the
|
159
|
+
* bucket range is 1; they're all the same. */
|
160
|
+
static const struct timer *find_first(const struct list_head *list,
|
161
|
+
unsigned int level,
|
162
|
+
const struct timer *prev)
|
163
|
+
{
|
164
|
+
struct timer *t;
|
165
|
+
|
166
|
+
list_for_each(list, t, list) {
|
167
|
+
if (!prev || t->time < prev->time)
|
168
|
+
prev = t;
|
169
|
+
if (level == 0)
|
170
|
+
break;
|
171
|
+
}
|
172
|
+
return prev;
|
173
|
+
}
|
174
|
+
|
175
|
+
/* Update level's first watermark, and return overall first. */
|
176
|
+
static const struct timer *first_for_level(struct timers *timers,
|
177
|
+
size_t level,
|
178
|
+
const struct timer *level_first,
|
179
|
+
const struct timer *first)
|
180
|
+
{
|
181
|
+
if (level_first) {
|
182
|
+
timers->firsts[level] = level_first->time;
|
183
|
+
if (!first || level_first->time < first->time)
|
184
|
+
first = level_first;
|
185
|
+
} else {
|
186
|
+
timers->firsts[level] = -1ULL;
|
187
|
+
}
|
188
|
+
return first;
|
189
|
+
}
|
190
|
+
|
191
|
+
static bool level_may_beat(const struct timers *timers, size_t level,
|
192
|
+
const struct timer *first)
|
193
|
+
{
|
194
|
+
return !first || timers->firsts[level] < first->time;
|
195
|
+
}
|
196
|
+
|
197
|
+
/* FIXME: Suboptimal */
|
198
|
+
static const struct timer *brute_force_first(struct timers *timers)
|
199
|
+
{
|
200
|
+
unsigned int l, i;
|
201
|
+
const struct timer *found = NULL;
|
202
|
+
|
203
|
+
for (l = 0; l < ARRAY_SIZE(timers->level) && timers->level[l]; l++) {
|
204
|
+
const struct timer *t = NULL;
|
205
|
+
|
206
|
+
/* Do we know they don't have a better one? */
|
207
|
+
if (!level_may_beat(timers, l, found))
|
208
|
+
continue;
|
209
|
+
|
210
|
+
/* Find first timer on this level. */
|
211
|
+
for (i = 0; i < PER_LEVEL; i++)
|
212
|
+
t = find_first(&timers->level[l]->list[i], l, t);
|
213
|
+
|
214
|
+
found = first_for_level(timers, l, t, found);
|
215
|
+
}
|
216
|
+
|
217
|
+
/* Check (and update) far list if there's a chance. */
|
218
|
+
l = ARRAY_SIZE(timers->level);
|
219
|
+
if (level_may_beat(timers, l, found)) {
|
220
|
+
const struct timer *t = find_first(&timers->far, l, NULL);
|
221
|
+
found = first_for_level(timers, l, t, found);
|
222
|
+
}
|
223
|
+
|
224
|
+
return found;
|
225
|
+
}
|
226
|
+
|
227
|
+
static const struct timer *get_first(struct timers *timers)
|
228
|
+
{
|
229
|
+
/* We can have just far timers, for example. */
|
230
|
+
if (timers->level[0]) {
|
231
|
+
/* First search rest of lower buckets; we've already spilled
|
232
|
+
* so if we find one there we don't need to search further. */
|
233
|
+
unsigned int i, off = timers->base % PER_LEVEL;
|
234
|
+
|
235
|
+
for (i = off; i < PER_LEVEL; i++) {
|
236
|
+
struct list_head *h = &timers->level[0]->list[i];
|
237
|
+
if (!list_empty(h))
|
238
|
+
return find_first(h, 0, NULL);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
/* From here on, we're searching non-normalized parts of the
|
243
|
+
* data structure, which is much subtler.
|
244
|
+
*
|
245
|
+
* So we brute force. */
|
246
|
+
return brute_force_first(timers);
|
247
|
+
}
|
248
|
+
|
249
|
+
static bool update_first(struct timers *timers)
|
250
|
+
{
|
251
|
+
const struct timer *found = get_first(timers);
|
252
|
+
|
253
|
+
if (!found) {
|
254
|
+
timers->first = -1ULL;
|
255
|
+
return false;
|
256
|
+
}
|
257
|
+
|
258
|
+
timers->first = found->time;
|
259
|
+
return true;
|
260
|
+
}
|
261
|
+
|
262
|
+
bool timer_earliest(struct timers *timers, struct timemono *first)
|
263
|
+
{
|
264
|
+
if (!update_first(timers))
|
265
|
+
return false;
|
266
|
+
|
267
|
+
*first = grains_to_time(timers->first);
|
268
|
+
return true;
|
269
|
+
}
|
270
|
+
|
271
|
+
/* Assume no timers before 'time', cascade down and update base time. */
|
272
|
+
static void timer_fast_forward(struct timers *timers, uint64_t time)
|
273
|
+
{
|
274
|
+
unsigned int level, changed;
|
275
|
+
int need_level = -1;
|
276
|
+
struct list_head list;
|
277
|
+
struct timer *i;
|
278
|
+
|
279
|
+
/* How many bits changed between base and time?
|
280
|
+
* Each time we wrap, we need to empty buckets from above. */
|
281
|
+
if (time == timers->base)
|
282
|
+
return;
|
283
|
+
|
284
|
+
changed = ilog64_nz(time ^ timers->base);
|
285
|
+
level = (changed - 1) / TIMER_LEVEL_BITS;
|
286
|
+
|
287
|
+
/* Buckets always empty downwards, so we could cascade manually,
|
288
|
+
* but it's rarely very many so we just remove and re-add */
|
289
|
+
list_head_init(&list);
|
290
|
+
|
291
|
+
do {
|
292
|
+
if (!timers->level[level]) {
|
293
|
+
/* We need any which belong on this level. */
|
294
|
+
timers_far_get(timers, &list,
|
295
|
+
timers->base
|
296
|
+
+ (1ULL << ((level+1)*TIMER_LEVEL_BITS))-1);
|
297
|
+
need_level = level;
|
298
|
+
} else {
|
299
|
+
unsigned src;
|
300
|
+
|
301
|
+
/* Get all timers from this bucket. */
|
302
|
+
src = (time >> (level * TIMER_LEVEL_BITS)) % PER_LEVEL;
|
303
|
+
list_append_list(&list,
|
304
|
+
&timers->level[level]->list[src]);
|
305
|
+
}
|
306
|
+
} while (level--);
|
307
|
+
|
308
|
+
/* Did we hit the last level? If so, add. */
|
309
|
+
if (need_level != -1)
|
310
|
+
add_level(timers, need_level);
|
311
|
+
|
312
|
+
/* Fast-forward the time, and re-add everyone. */
|
313
|
+
timers->base = time;
|
314
|
+
while ((i = list_pop(&list, struct timer, list)) != NULL)
|
315
|
+
timer_add_raw(timers, i);
|
316
|
+
}
|
317
|
+
|
318
|
+
/* Returns an expired timer. */
|
319
|
+
struct timer *timers_expire(struct timers *timers, struct timemono expire)
|
320
|
+
{
|
321
|
+
uint64_t now = time_to_grains(expire);
|
322
|
+
unsigned int off;
|
323
|
+
struct timer *t;
|
324
|
+
|
325
|
+
assert(now >= timers->base);
|
326
|
+
|
327
|
+
if (!timers->level[0]) {
|
328
|
+
if (list_empty(&timers->far))
|
329
|
+
return NULL;
|
330
|
+
add_level(timers, 0);
|
331
|
+
}
|
332
|
+
|
333
|
+
do {
|
334
|
+
if (timers->first > now) {
|
335
|
+
timer_fast_forward(timers, now);
|
336
|
+
return NULL;
|
337
|
+
}
|
338
|
+
|
339
|
+
timer_fast_forward(timers, timers->first);
|
340
|
+
off = timers->base % PER_LEVEL;
|
341
|
+
|
342
|
+
/* This *may* be NULL, if we deleted the first timer */
|
343
|
+
t = list_pop(&timers->level[0]->list[off], struct timer, list);
|
344
|
+
if (t)
|
345
|
+
list_node_init(&t->list);
|
346
|
+
} while (!t && update_first(timers));
|
347
|
+
|
348
|
+
return t;
|
349
|
+
}
|
350
|
+
|
351
|
+
static bool timer_list_check(const struct list_head *l,
|
352
|
+
uint64_t min, uint64_t max, uint64_t first,
|
353
|
+
const char *abortstr)
|
354
|
+
{
|
355
|
+
const struct timer *t;
|
356
|
+
|
357
|
+
if (!list_check(l, abortstr))
|
358
|
+
return false;
|
359
|
+
|
360
|
+
list_for_each(l, t, list) {
|
361
|
+
if (t->time < min || t->time > max) {
|
362
|
+
if (abortstr) {
|
363
|
+
fprintf(stderr,
|
364
|
+
"%s: timer %p %llu not %llu-%llu\n",
|
365
|
+
abortstr, t, (long long)t->time,
|
366
|
+
(long long)min, (long long)max);
|
367
|
+
abort();
|
368
|
+
}
|
369
|
+
return false;
|
370
|
+
}
|
371
|
+
if (t->time < first) {
|
372
|
+
if (abortstr) {
|
373
|
+
fprintf(stderr,
|
374
|
+
"%s: timer %p %llu < minimum %llu\n",
|
375
|
+
abortstr, t, (long long)t->time,
|
376
|
+
(long long)first);
|
377
|
+
abort();
|
378
|
+
}
|
379
|
+
return false;
|
380
|
+
}
|
381
|
+
}
|
382
|
+
return true;
|
383
|
+
}
|
384
|
+
|
385
|
+
struct timers *timers_check(const struct timers *timers, const char *abortstr)
|
386
|
+
{
|
387
|
+
unsigned int l, i, off;
|
388
|
+
uint64_t base;
|
389
|
+
|
390
|
+
l = 0;
|
391
|
+
if (!timers->level[0])
|
392
|
+
goto past_levels;
|
393
|
+
|
394
|
+
/* First level is simple. */
|
395
|
+
off = timers->base % PER_LEVEL;
|
396
|
+
for (i = 0; i < PER_LEVEL; i++) {
|
397
|
+
struct list_head *h;
|
398
|
+
|
399
|
+
h = &timers->level[l]->list[(i+off) % PER_LEVEL];
|
400
|
+
if (!timer_list_check(h, timers->base + i, timers->base + i,
|
401
|
+
timers->firsts[l], abortstr))
|
402
|
+
return NULL;
|
403
|
+
}
|
404
|
+
|
405
|
+
/* For other levels, "current" bucket has been emptied, and may contain
|
406
|
+
* entries for the current + level_size bucket. */
|
407
|
+
for (l = 1; l < ARRAY_SIZE(timers->level) && timers->level[l]; l++) {
|
408
|
+
uint64_t per_bucket = 1ULL << (TIMER_LEVEL_BITS * l);
|
409
|
+
|
410
|
+
off = ((timers->base >> (l*TIMER_LEVEL_BITS)) % PER_LEVEL);
|
411
|
+
/* We start at *next* bucket. */
|
412
|
+
base = (timers->base & ~(per_bucket - 1)) + per_bucket;
|
413
|
+
|
414
|
+
for (i = 1; i <= PER_LEVEL; i++) {
|
415
|
+
struct list_head *h;
|
416
|
+
|
417
|
+
h = &timers->level[l]->list[(i+off) % PER_LEVEL];
|
418
|
+
if (!timer_list_check(h, base, base + per_bucket - 1,
|
419
|
+
timers->firsts[l], abortstr))
|
420
|
+
return NULL;
|
421
|
+
base += per_bucket;
|
422
|
+
}
|
423
|
+
}
|
424
|
+
|
425
|
+
past_levels:
|
426
|
+
base = (timers->base & ~((1ULL << (TIMER_LEVEL_BITS * l)) - 1))
|
427
|
+
+ (1ULL << (TIMER_LEVEL_BITS * l)) - 1;
|
428
|
+
if (!timer_list_check(&timers->far, base, -1ULL,
|
429
|
+
timers->firsts[ARRAY_SIZE(timers->level)],
|
430
|
+
abortstr))
|
431
|
+
return NULL;
|
432
|
+
|
433
|
+
return (struct timers *)timers;
|
434
|
+
}
|
435
|
+
|
436
|
+
#ifdef CCAN_TIMER_DEBUG
|
437
|
+
static void dump_bucket_stats(FILE *fp, const struct list_head *h)
|
438
|
+
{
|
439
|
+
unsigned long long min, max, num;
|
440
|
+
struct timer *t;
|
441
|
+
|
442
|
+
if (list_empty(h)) {
|
443
|
+
printf("\n");
|
444
|
+
return;
|
445
|
+
}
|
446
|
+
|
447
|
+
min = -1ULL;
|
448
|
+
max = 0;
|
449
|
+
num = 0;
|
450
|
+
list_for_each(h, t, list) {
|
451
|
+
if (t->time < min)
|
452
|
+
min = t->time;
|
453
|
+
if (t->time > max)
|
454
|
+
max = t->time;
|
455
|
+
num++;
|
456
|
+
}
|
457
|
+
fprintf(fp, " %llu (%llu-%llu)\n",
|
458
|
+
num, min, max);
|
459
|
+
}
|
460
|
+
|
461
|
+
void timers_dump(const struct timers *timers, FILE *fp)
|
462
|
+
{
|
463
|
+
unsigned int l, i, off;
|
464
|
+
unsigned long long base;
|
465
|
+
|
466
|
+
if (!fp)
|
467
|
+
fp = stderr;
|
468
|
+
|
469
|
+
fprintf(fp, "Base: %llu\n", (unsigned long long)timers->base);
|
470
|
+
|
471
|
+
if (!timers->level[0])
|
472
|
+
goto past_levels;
|
473
|
+
|
474
|
+
fprintf(fp, "Level 0:\n");
|
475
|
+
|
476
|
+
/* First level is simple. */
|
477
|
+
off = timers->base % PER_LEVEL;
|
478
|
+
for (i = 0; i < PER_LEVEL; i++) {
|
479
|
+
const struct list_head *h;
|
480
|
+
|
481
|
+
fprintf(fp, " Bucket %llu (%lu):",
|
482
|
+
(i+off) % PER_LEVEL, timers->base + i);
|
483
|
+
h = &timers->level[0]->list[(i+off) % PER_LEVEL];
|
484
|
+
dump_bucket_stats(fp, h);
|
485
|
+
}
|
486
|
+
|
487
|
+
/* For other levels, "current" bucket has been emptied, and may contain
|
488
|
+
* entries for the current + level_size bucket. */
|
489
|
+
for (l = 1; l < ARRAY_SIZE(timers->level) && timers->level[l]; l++) {
|
490
|
+
uint64_t per_bucket = 1ULL << (TIMER_LEVEL_BITS * l);
|
491
|
+
|
492
|
+
off = ((timers->base >> (l*TIMER_LEVEL_BITS)) % PER_LEVEL);
|
493
|
+
/* We start at *next* bucket. */
|
494
|
+
base = (timers->base & ~(per_bucket - 1)) + per_bucket;
|
495
|
+
|
496
|
+
fprintf(fp, "Level %u:\n", l);
|
497
|
+
for (i = 1; i <= PER_LEVEL; i++) {
|
498
|
+
const struct list_head *h;
|
499
|
+
|
500
|
+
fprintf(fp, " Bucket %llu (%llu - %llu):",
|
501
|
+
(i+off) % PER_LEVEL,
|
502
|
+
base, base + per_bucket - 1);
|
503
|
+
|
504
|
+
h = &timers->level[l]->list[(i+off) % PER_LEVEL];
|
505
|
+
dump_bucket_stats(fp, h);
|
506
|
+
base += per_bucket;
|
507
|
+
}
|
508
|
+
}
|
509
|
+
|
510
|
+
past_levels:
|
511
|
+
if (!list_empty(&timers->far)) {
|
512
|
+
fprintf(fp, "Far timers:");
|
513
|
+
dump_bucket_stats(fp, &timers->far);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
#endif
|
517
|
+
|
518
|
+
void timers_cleanup(struct timers *timers)
|
519
|
+
{
|
520
|
+
unsigned int l;
|
521
|
+
|
522
|
+
for (l = 0; l < ARRAY_SIZE(timers->level); l++)
|
523
|
+
free(timers->level[l]);
|
524
|
+
}
|