timeout_ext 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|