1 /*
2 * uhub - A tiny ADC p2p connection hub
3 * Copyright (C) 2007-2014, Jan Vidar Krey
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along wtimeout_evtith this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "uhub.h"
21
timeout_evt_initialize(struct timeout_evt * t,timeout_evt_cb cb,void * ptr)22 void timeout_evt_initialize(struct timeout_evt* t, timeout_evt_cb cb, void* ptr)
23 {
24 t->callback = cb;
25 t->ptr = ptr;
26 t->prev = 0;
27 t->next = 0;
28 }
29
timeout_evt_reset(struct timeout_evt * t)30 void timeout_evt_reset(struct timeout_evt* t)
31 {
32 t->prev = 0;
33 t->next = 0;
34 }
35
timeout_evt_is_scheduled(struct timeout_evt * t)36 int timeout_evt_is_scheduled(struct timeout_evt* t)
37 {
38 return t->prev != NULL;
39 }
40
timeout_queue_initialize(struct timeout_queue * t,time_t now,size_t max)41 void timeout_queue_initialize(struct timeout_queue* t, time_t now, size_t max)
42 {
43 t->last = now;
44 t->max = max;
45 memset(&t->lock, 0, sizeof(t->lock));
46 t->events = hub_malloc_zero(max * sizeof(struct timeout_evt*));
47 }
48
timeout_queue_shutdown(struct timeout_queue * t)49 void timeout_queue_shutdown(struct timeout_queue* t)
50 {
51 hub_free(t->events);
52 t->events = 0;
53 t->max = 0;
54 }
55
timeout_queue_locked(struct timeout_queue * t)56 static int timeout_queue_locked(struct timeout_queue* t)
57 {
58 return t->lock.ptr != NULL;
59 }
60
timeout_queue_lock(struct timeout_queue * t)61 static void timeout_queue_lock(struct timeout_queue* t)
62 {
63 t->lock.ptr = t;
64 }
65
66 // unlock and flush the locked events to the main timeout queue.
timeout_queue_unlock(struct timeout_queue * t)67 static void timeout_queue_unlock(struct timeout_queue* t)
68 {
69 struct timeout_evt* evt, *tmp, *first;
70 size_t pos;
71 t->lock.ptr = NULL;
72
73 evt = t->lock.next;
74 while (evt)
75 {
76 tmp = evt->next;
77 pos = evt->timestamp % t->max;
78 first = t->events[pos];
79 if (first)
80 {
81 first->prev->next = evt;
82 evt->prev = first->prev;
83 first->prev = evt;
84 }
85 else
86 {
87 t->events[pos] = evt;
88 evt->prev = evt;
89 }
90 evt->next = 0;
91 evt = tmp;
92 }
93
94 t->lock.next = 0;
95 t->lock.prev = 0;
96 }
97
98
timeout_queue_process(struct timeout_queue * t,time_t now)99 size_t timeout_queue_process(struct timeout_queue* t, time_t now)
100 {
101 size_t pos = (size_t) t->last;
102 size_t events = 0;
103 struct timeout_evt* evt = 0;
104 t->last = now;
105 timeout_queue_lock(t);
106 for (; pos <= now; pos++)
107 {
108 while ((evt = t->events[pos % t->max]))
109 {
110 timeout_queue_remove(t, evt);
111 evt->callback(evt);
112 events++;
113 }
114 }
115 timeout_queue_unlock(t);
116 return events;
117 }
118
timeout_queue_get_next_timeout(struct timeout_queue * t,time_t now)119 size_t timeout_queue_get_next_timeout(struct timeout_queue* t, time_t now)
120 {
121 size_t seconds = 0;
122 while (t->events[(now + seconds) % t->max] == NULL && seconds < t->max)
123 {
124 seconds++;
125 }
126 if (seconds == 0)
127 return 1;
128 return seconds;
129 }
130
timeout_queue_insert_locked(struct timeout_queue * t,struct timeout_evt * evt)131 static void timeout_queue_insert_locked(struct timeout_queue* t, struct timeout_evt* evt)
132 {
133 /* All events point back to the sentinel.
134 * this means the event is considered schedule (see timeout_evt_is_scheduled),
135 * and it is easy to tell if the event is in the wait queue or not.
136 */
137 evt->prev = &t->lock;
138 evt->next = NULL;
139
140 // The sentinel next points to the first event in the locked queue
141 // The sentinel prev points to the last evetnt in the locked queue.
142 // NOTE: if prev is != NULL then next also must be != NULL.
143 if (t->lock.prev)
144 {
145 t->lock.prev->next = evt;
146 t->lock.prev = evt;
147 }
148 else
149 {
150 t->lock.next = evt;
151 t->lock.prev = evt;
152 }
153 return;
154 }
155
timeout_queue_remove_locked(struct timeout_queue * t,struct timeout_evt * evt)156 static void timeout_queue_remove_locked(struct timeout_queue* t, struct timeout_evt* evt)
157 {
158 uhub_assert(evt->prev == &t->lock);
159 if (t->lock.next == evt)
160 {
161 t->lock.next = evt->next;
162 if (t->lock.prev == evt)
163 t->lock.prev = evt->next;
164 }
165 else
166 {
167 struct timeout_evt *prev, *it;
168 prev = 0;
169 it = t->lock.next;
170 while (it)
171 {
172 prev = it;
173 it = it->next;
174 if (it == evt)
175 {
176 prev->next = it->next;
177 if (!prev->next)
178 t->lock.prev = prev;
179 }
180 }
181 }
182 timeout_evt_reset(evt);
183 }
184
185
186
timeout_queue_insert(struct timeout_queue * t,struct timeout_evt * evt,size_t seconds)187 void timeout_queue_insert(struct timeout_queue* t, struct timeout_evt* evt, size_t seconds)
188 {
189 struct timeout_evt* first;
190 size_t pos = ((t->last + seconds) % t->max);
191 evt->timestamp = t->last + seconds;
192 evt->next = 0;
193
194 if (timeout_queue_locked(t))
195 {
196 timeout_queue_insert_locked(t, evt);
197 return;
198 }
199
200 first = t->events[pos];
201
202 if (first)
203 {
204 uhub_assert(first->timestamp == evt->timestamp);
205 first->prev->next = evt;
206 evt->prev = first->prev;
207 first->prev = evt;
208 }
209 else
210 {
211 t->events[pos] = evt;
212 evt->prev = evt;
213 }
214 evt->next = 0;
215 }
216
timeout_queue_remove(struct timeout_queue * t,struct timeout_evt * evt)217 void timeout_queue_remove(struct timeout_queue* t, struct timeout_evt* evt)
218 {
219 size_t pos = (evt->timestamp % t->max);
220 struct timeout_evt* first = t->events[pos];
221
222 // Removing a locked event
223 if (evt->prev == &t->lock)
224 {
225 timeout_queue_remove_locked(t, evt);
226 return;
227 }
228
229 if (!first || !evt->prev)
230 return;
231
232 if (first == evt)
233 {
234 if (first->prev != first)
235 {
236 t->events[pos] = first->next;
237 t->events[pos]->prev = evt->prev;
238 }
239 else
240 {
241 t->events[pos] = 0;
242 }
243 }
244 else if (evt == first->prev)
245 {
246 first->prev = evt->prev;
247 evt->prev->next = 0;
248 }
249 else
250 {
251 evt->prev->next = evt->next;
252 evt->next->prev = evt->prev;
253 }
254 timeout_evt_reset(evt);
255 }
256
timeout_queue_reschedule(struct timeout_queue * t,struct timeout_evt * evt,size_t seconds)257 void timeout_queue_reschedule(struct timeout_queue* t, struct timeout_evt* evt, size_t seconds)
258 {
259 if (timeout_evt_is_scheduled(evt))
260 timeout_queue_remove(t, evt);
261 timeout_queue_insert(t, evt, seconds);
262 }
263
264