Skip to content

Commit 11dd5a0

Browse files
committed
lib: add option to start/stop wheel timer
Fix: This fix add option to start and stop a wheel timer. To handle the case, to not to run the wheel timer unnecessarily when there is no element in it, a new api wheel_stop() is added. Also whenever a new element is added to wheel timer's slot, that wheel timer is tried to be started again, if not running already. During testing, certain behavior found, if a wheel timer is fired/an event is scheduled in the middle of configuration, it brings some random pattern/delay that sometimes delays "Verifying BGP Convergence on router r1" for ~3 sec. So a new argument added to in wheel_init, to pass wait time before the wheel timer is started. For fast wheel timer, it works fine 10 ms. Just to note, this is just one time/start time delay, after that wheel schedules events, according existing logic as usual. If passed wait time is 0, then it will schedule/start the wheel timer according to existing logic Signed-off-by: Soumya Roy <[email protected]>
1 parent 8fcb010 commit 11dd5a0

File tree

3 files changed

+62
-17
lines changed

3 files changed

+62
-17
lines changed

Diff for: lib/wheel.c

+46-9
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL_LIST, "Timer Wheel Slot List");
1717
static int debug_timer_wheel = 0;
1818

1919
static void wheel_timer_thread(struct event *t);
20+
static void wheel_stop(struct timer_wheel *wheel);
21+
static void wheel_start(struct timer_wheel *wheel);
2022

2123
static void wheel_timer_thread(struct event *t)
2224
{
2325
struct listnode *node, *nextnode;
2426
unsigned long long curr_slot;
25-
unsigned int slots_to_skip = 1;
27+
int slots_to_skip = 1;
2628
struct timer_wheel *wheel;
2729
void *data;
2830

@@ -47,14 +49,27 @@ static void wheel_timer_thread(struct event *t)
4749
slots_to_skip++;
4850

4951
wheel->slots_to_skip = slots_to_skip;
52+
if ((((curr_slot + slots_to_skip) % wheel->slots) == curr_slot) &&
53+
list_isempty(wheel->wheel_slot_lists[curr_slot])) {
54+
/* Came to back to same slot and that is empty
55+
* so the wheel is empty, stop it
56+
*/
57+
if (!wheel->run_forever) {
58+
wheel_stop(wheel);
59+
if (debug_timer_wheel)
60+
zlog_debug("Stopped an empty wheel %p", wheel);
61+
62+
return;
63+
}
64+
}
65+
5066
event_add_timer_msec(wheel->master, wheel_timer_thread, wheel,
5167
wheel->nexttime * slots_to_skip, &wheel->timer);
5268
}
5369

54-
struct timer_wheel *wheel_init(struct event_loop *master, int period,
55-
size_t slots,
56-
unsigned int (*slot_key)(const void *),
57-
void (*slot_run)(void *), const char *run_name)
70+
struct timer_wheel *wheel_init(struct event_loop *master, int period, size_t slots, int start_wait,
71+
unsigned int (*slot_key)(const void *), void (*slot_run)(void *),
72+
const char *run_name, bool run_forever)
5873
{
5974
struct timer_wheel *wheel;
6075
size_t i;
@@ -63,20 +78,21 @@ struct timer_wheel *wheel_init(struct event_loop *master, int period,
6378

6479
wheel->slot_key = slot_key;
6580
wheel->slot_run = slot_run;
66-
6781
wheel->period = period;
6882
wheel->slots = slots;
6983
wheel->curr_slot = 0;
7084
wheel->master = master;
7185
wheel->nexttime = period / slots;
86+
wheel->start_wait = start_wait;
87+
wheel->run_forever = run_forever;
7288

7389
wheel->wheel_slot_lists = XCALLOC(MTYPE_TIMER_WHEEL_LIST,
7490
slots * sizeof(struct list *));
7591
for (i = 0; i < slots; i++)
7692
wheel->wheel_slot_lists[i] = list_new();
7793

78-
event_add_timer_msec(wheel->master, wheel_timer_thread, wheel,
79-
wheel->nexttime, &wheel->timer);
94+
if (wheel->run_forever)
95+
wheel_start(wheel);
8096

8197
return wheel;
8298
}
@@ -104,7 +120,7 @@ int wheel_add_item(struct timer_wheel *wheel, void *item)
104120
zlog_debug("%s: Inserting %p: %lld %lld", __func__, item, slot,
105121
slot % wheel->slots);
106122
listnode_add(wheel->wheel_slot_lists[slot % wheel->slots], item);
107-
123+
wheel_start(wheel);
108124
return 0;
109125
}
110126

@@ -121,3 +137,24 @@ int wheel_remove_item(struct timer_wheel *wheel, void *item)
121137

122138
return 0;
123139
}
140+
141+
static void wheel_stop(struct timer_wheel *wheel)
142+
{
143+
EVENT_OFF(wheel->timer);
144+
}
145+
146+
static void wheel_start(struct timer_wheel *wheel)
147+
{
148+
int first_schedule_time = wheel->nexttime;
149+
150+
if (wheel->start_wait)
151+
first_schedule_time = wheel->start_wait;
152+
153+
if (!event_is_scheduled(wheel->timer)) {
154+
if (debug_timer_wheel)
155+
zlog_info("started a wheel timer %p", wheel);
156+
157+
event_add_timer_msec(wheel->master, wheel_timer_thread, wheel, first_schedule_time,
158+
&wheel->timer);
159+
}
160+
}

Diff for: lib/wheel.h

+14-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ struct timer_wheel {
1717
long long curr_slot;
1818
unsigned int period;
1919
unsigned int nexttime;
20-
unsigned int slots_to_skip;
20+
int slots_to_skip;
21+
int start_wait;
22+
bool run_forever;
2123

2224
struct list **wheel_slot_lists;
2325
struct event *timer;
@@ -38,10 +40,18 @@ struct timer_wheel {
3840
* for items in each slot
3941
* slots - The number of slots to have in this particular
4042
* timer wheel
43+
*
44+
* start_wait - Initial start wait in ms, before the wheel timer
45+
* is started first time, if no wait, pass 0
46+
*
4147
* slot_key - A hashing function of some sort that will allow
4248
* the timer wheel to put items into individual slots
4349
* slot_run - The function to run over each item in a particular slot
4450
*
51+
* run_forever - If this wheel timer is supposed to run forever,
52+
* otherwise this flag will be used to pause the timer
53+
* if no item left in wheel slots.
54+
*
4555
* Creates a timer wheel that will wake up 'slots' times over the entire
4656
* wheel. Each time the timer wheel wakes up it will iterate through
4757
* and run the slot_run function for each item stored in that particular
@@ -65,10 +75,9 @@ struct timer_wheel {
6575
* and cause significant amount of time handling thread events instead
6676
* of running your code.
6777
*/
68-
struct timer_wheel *wheel_init(struct event_loop *master, int period,
69-
size_t slots,
70-
unsigned int (*slot_key)(const void *),
71-
void (*slot_run)(void *), const char *run_name);
78+
struct timer_wheel *wheel_init(struct event_loop *master, int period, size_t slots, int start_wait,
79+
unsigned int (*slot_key)(const void *), void (*slot_run)(void *),
80+
const char *run_name, bool run_forever);
7281

7382
/*
7483
* Delete the specified timer wheel created

Diff for: pimd/pim_upstream.c

+2-3
Original file line numberDiff line numberDiff line change
@@ -2231,9 +2231,8 @@ void pim_upstream_init(struct pim_instance *pim)
22312231
char name[64];
22322232

22332233
snprintf(name, sizeof(name), "PIM %s Timer Wheel", pim->vrf->name);
2234-
pim->upstream_sg_wheel =
2235-
wheel_init(router->master, 31000, 100, pim_upstream_hash_key,
2236-
pim_upstream_sg_running, name);
2234+
pim->upstream_sg_wheel = wheel_init(router->master, 31000, 100, 0, pim_upstream_hash_key,
2235+
pim_upstream_sg_running, name, true);
22372236

22382237
rb_pim_upstream_init(&pim->upstream_head);
22392238
}

0 commit comments

Comments
 (0)