1 /*
2 * Copyright (c) 2015-2016 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Jan Friesse (jfriesse@redhat.com)
7 *
8 * This software licensed under BSD license, the text of which follows:
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <string.h>
36
37 #include "timer-list.h"
38
39 void
timer_list_init(struct timer_list * tlist)40 timer_list_init(struct timer_list *tlist)
41 {
42
43 memset(tlist, 0, sizeof(*tlist));
44
45 TAILQ_INIT(&tlist->list);
46 TAILQ_INIT(&tlist->free_list);
47 }
48
49 static PRIntervalTime
timer_list_entry_time_to_expire(const struct timer_list_entry * entry,PRIntervalTime current_time)50 timer_list_entry_time_to_expire(const struct timer_list_entry *entry, PRIntervalTime current_time)
51 {
52 PRIntervalTime diff, half_interval;
53
54 diff = entry->expire_time - current_time;
55 half_interval = ~0;
56 half_interval /= 2;
57
58 if (diff > half_interval) {
59 return (0);
60 }
61
62 return (diff);
63 }
64
65 static int
timer_list_entry_cmp(const struct timer_list_entry * entry1,const struct timer_list_entry * entry2,PRIntervalTime current_time)66 timer_list_entry_cmp(const struct timer_list_entry *entry1,
67 const struct timer_list_entry *entry2, PRIntervalTime current_time)
68 {
69 PRIntervalTime diff1, diff2;
70 int res;
71
72 diff1 = timer_list_entry_time_to_expire(entry1, current_time);
73 diff2 = timer_list_entry_time_to_expire(entry2, current_time);
74
75 res = 0;
76
77 if (diff1 < diff2) res = -1;
78 if (diff1 > diff2) res = 1;
79
80 return (res);
81 }
82
83 static void
timer_list_insert_into_list(struct timer_list * tlist,struct timer_list_entry * new_entry)84 timer_list_insert_into_list(struct timer_list *tlist, struct timer_list_entry *new_entry)
85 {
86 struct timer_list_entry *entry;
87
88 /*
89 * This can overflow and it's not a problem
90 */
91 new_entry->expire_time = new_entry->epoch + PR_MillisecondsToInterval(new_entry->interval);
92
93 entry = TAILQ_FIRST(&tlist->list);
94 while (entry != NULL) {
95 if (timer_list_entry_cmp(entry, new_entry, new_entry->epoch) > 0) {
96 /*
97 * Insert new entry right before current entry
98 */
99 TAILQ_INSERT_BEFORE(entry, new_entry, entries);
100
101 break;
102 }
103
104 entry = TAILQ_NEXT(entry, entries);
105 }
106
107 if (entry == NULL) {
108 TAILQ_INSERT_TAIL(&tlist->list, new_entry, entries);
109 }
110 }
111
112 struct timer_list_entry *
timer_list_add(struct timer_list * tlist,PRUint32 interval,timer_list_cb_fn func,void * data1,void * data2)113 timer_list_add(struct timer_list *tlist, PRUint32 interval, timer_list_cb_fn func, void *data1,
114 void *data2)
115 {
116 struct timer_list_entry *new_entry;
117
118 if (interval < 1 || interval > TIMER_LIST_MAX_INTERVAL) {
119 return (NULL);
120 }
121
122 if (!TAILQ_EMPTY(&tlist->free_list)) {
123 /*
124 * Use free list entry
125 */
126 new_entry = TAILQ_FIRST(&tlist->free_list);
127 TAILQ_REMOVE(&tlist->free_list, new_entry, entries);
128 } else {
129 /*
130 * Alloc new entry
131 */
132 new_entry = malloc(sizeof(*new_entry));
133 if (new_entry == NULL) {
134 return (NULL);
135 }
136 }
137
138 memset(new_entry, 0, sizeof(*new_entry));
139 new_entry->epoch = PR_IntervalNow();
140 new_entry->interval = interval;
141 new_entry->func = func;
142 new_entry->user_data1 = data1;
143 new_entry->user_data2 = data2;
144 new_entry->is_active = 1;
145
146 timer_list_insert_into_list(tlist, new_entry);
147
148 return (new_entry);
149 }
150
151 void
timer_list_reschedule(struct timer_list * tlist,struct timer_list_entry * entry)152 timer_list_reschedule(struct timer_list *tlist, struct timer_list_entry *entry)
153 {
154
155 if (entry->is_active) {
156 entry->epoch = PR_IntervalNow();
157 TAILQ_REMOVE(&tlist->list, entry, entries);
158 timer_list_insert_into_list(tlist, entry);
159 }
160 }
161
162 void
timer_list_expire(struct timer_list * tlist)163 timer_list_expire(struct timer_list *tlist)
164 {
165 PRIntervalTime now;
166 struct timer_list_entry *entry;
167 int res;
168
169 now = PR_IntervalNow();
170
171 while ((entry = TAILQ_FIRST(&tlist->list)) != NULL &&
172 timer_list_entry_time_to_expire(entry, now) == 0) {
173 /*
174 * Expired
175 */
176 res = entry->func(entry->user_data1, entry->user_data2);
177 if (res == 0) {
178 /*
179 * Move item to free list
180 */
181 timer_list_delete(tlist, entry);
182 } else if (entry->is_active) {
183 /*
184 * Schedule again
185 */
186 entry->epoch = now;
187 TAILQ_REMOVE(&tlist->list, entry, entries);
188 timer_list_insert_into_list(tlist, entry);
189 }
190 }
191 }
192
193 PRIntervalTime
timer_list_time_to_expire(struct timer_list * tlist)194 timer_list_time_to_expire(struct timer_list *tlist)
195 {
196 struct timer_list_entry *entry;
197
198 entry = TAILQ_FIRST(&tlist->list);
199 if (entry == NULL) {
200 return (PR_INTERVAL_NO_TIMEOUT);
201 }
202
203 return (timer_list_entry_time_to_expire(entry, PR_IntervalNow()));
204 }
205
206 uint32_t
timer_list_time_to_expire_ms(struct timer_list * tlist)207 timer_list_time_to_expire_ms(struct timer_list *tlist)
208 {
209 struct timer_list_entry *entry;
210 uint32_t u32;
211
212 entry = TAILQ_FIRST(&tlist->list);
213 if (entry == NULL) {
214 u32 = ~((uint32_t)0);
215 return (u32);
216 }
217
218 return (PR_IntervalToMilliseconds(timer_list_entry_time_to_expire(entry, PR_IntervalNow())));
219 }
220
221 void
timer_list_delete(struct timer_list * tlist,struct timer_list_entry * entry)222 timer_list_delete(struct timer_list *tlist, struct timer_list_entry *entry)
223 {
224
225 if (entry->is_active) {
226 /*
227 * Move item to free list
228 */
229 TAILQ_REMOVE(&tlist->list, entry, entries);
230 TAILQ_INSERT_HEAD(&tlist->free_list, entry, entries);
231 entry->is_active = 0;
232 }
233 }
234
235 void
timer_list_free(struct timer_list * tlist)236 timer_list_free(struct timer_list *tlist)
237 {
238 struct timer_list_entry *entry;
239 struct timer_list_entry *entry_next;
240
241
242 entry = TAILQ_FIRST(&tlist->list);
243
244 while (entry != NULL) {
245 entry_next = TAILQ_NEXT(entry, entries);
246
247 free(entry);
248
249 entry = entry_next;
250 }
251
252 entry = TAILQ_FIRST(&tlist->free_list);
253
254 while (entry != NULL) {
255 entry_next = TAILQ_NEXT(entry, entries);
256
257 free(entry);
258
259 entry = entry_next;
260 }
261
262 timer_list_init(tlist);
263 }
264