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