1 /*
2  * Copyright 2008-2014 Arsen Chaloyan
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * $Id: apt_timer_queue.c 2174 2014-09-12 03:33:16Z achaloyan@gmail.com $
17  */
18 
19 #ifdef WIN32
20 #pragma warning(disable: 4127)
21 #endif
22 #include <apr_ring.h>
23 #include "apt_timer_queue.h"
24 #include "apt_log.h"
25 
26 /** Timer queue */
27 struct apt_timer_queue_t {
28 	/** Ring head */
29 	APR_RING_HEAD(apt_timer_head_t, apt_timer_t) head;
30 
31 	/** Elapsed time */
32 	apr_uint32_t  elapsed_time;
33 };
34 
35 /** Timer */
36 struct apt_timer_t {
37 	/** Ring entry */
38 	APR_RING_ENTRY(apt_timer_t) link;
39 
40 	/** Back pointer to queue */
41 	apt_timer_queue_t   *queue;
42 	/** Time next report is scheduled at */
43 	apr_uint32_t         scheduled_time;
44 
45 	/** Timer proc */
46 	apt_timer_proc_f     proc;
47 	/** Timer object */
48 	void                *obj;
49 };
50 
51 static apt_bool_t apt_timer_insert(apt_timer_queue_t *timer_queue, apt_timer_t *timer);
52 static apt_bool_t apt_timer_remove(apt_timer_queue_t *timer_queue, apt_timer_t *timer);
53 static void apt_timers_reschedule(apt_timer_queue_t *timer_queue);
54 
55 /** Create timer queue */
apt_timer_queue_create(apr_pool_t * pool)56 APT_DECLARE(apt_timer_queue_t*) apt_timer_queue_create(apr_pool_t *pool)
57 {
58 	apt_timer_queue_t *timer_queue = apr_palloc(pool,sizeof(apt_timer_queue_t));
59 	APR_RING_INIT(&timer_queue->head, apt_timer_t, link);
60 	timer_queue->elapsed_time = 0;
61 	return timer_queue;
62 }
63 
64 /** Destroy timer queue */
apt_timer_queue_destroy(apt_timer_queue_t * timer_queue)65 APT_DECLARE(void) apt_timer_queue_destroy(apt_timer_queue_t *timer_queue)
66 {
67 	/* nothing to destroy */
68 }
69 
70 /** Advance scheduled timers */
apt_timer_queue_advance(apt_timer_queue_t * timer_queue,apr_uint32_t elapsed_time)71 APT_DECLARE(void) apt_timer_queue_advance(apt_timer_queue_t *timer_queue, apr_uint32_t elapsed_time)
72 {
73 	apt_timer_t *timer;
74 
75 	if(APR_RING_EMPTY(&timer_queue->head, apt_timer_t, link)) {
76 		/* just return, nothing to do */
77 		return;
78 	}
79 
80 	/* increment elapsed time */
81 	timer_queue->elapsed_time += elapsed_time;
82 	if(timer_queue->elapsed_time >= 0xFFFF) {
83 #ifdef APT_TIMER_DEBUG
84 		apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Reschedule Timers [%u]",timer_queue->elapsed_time);
85 #endif
86 		apt_timers_reschedule(timer_queue);
87 	}
88 
89 	/* process timers */
90 	do {
91 		/* get first node (timer) */
92 		timer = APR_RING_FIRST(&timer_queue->head);
93 
94 		if(timer->scheduled_time > timer_queue->elapsed_time) {
95 			/* scheduled time is not elapsed yet */
96 			break;
97 		}
98 
99 #ifdef APT_TIMER_DEBUG
100 		apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Timer Elapsed 0x%x [%u]",timer,timer->scheduled_time);
101 #endif
102 		/* remove the elapsed timer from the list */
103 		APR_RING_REMOVE(timer, link);
104 		timer->scheduled_time = 0;
105 		/* process the elapsed timer */
106 		timer->proc(timer,timer->obj);
107 	}
108 	while(!APR_RING_EMPTY(&timer_queue->head, apt_timer_t, link));
109 }
110 
111 /** Is timer queue empty */
apt_timer_queue_is_empty(const apt_timer_queue_t * timer_queue)112 APT_DECLARE(apt_bool_t) apt_timer_queue_is_empty(const apt_timer_queue_t *timer_queue)
113 {
114 	return APR_RING_EMPTY(&timer_queue->head, apt_timer_t, link) ? TRUE : FALSE;
115 }
116 
117 /** Get current timeout */
apt_timer_queue_timeout_get(const apt_timer_queue_t * timer_queue,apr_uint32_t * timeout)118 APT_DECLARE(apt_bool_t) apt_timer_queue_timeout_get(const apt_timer_queue_t *timer_queue, apr_uint32_t *timeout)
119 {
120 	apt_timer_t *timer;
121 	/* is queue empty */
122 	if(APR_RING_EMPTY(&timer_queue->head, apt_timer_t, link)) {
123 		return FALSE;
124 	}
125 
126 	/* get first node (timer) */
127 	timer = APR_RING_FIRST(&timer_queue->head);
128 	if(!timer) {
129 		return FALSE;
130 	}
131 
132 	*timeout = timer->scheduled_time - timer_queue->elapsed_time;
133 	return TRUE;
134 }
135 
136 /** Create timer */
apt_timer_create(apt_timer_queue_t * timer_queue,apt_timer_proc_f proc,void * obj,apr_pool_t * pool)137 APT_DECLARE(apt_timer_t*) apt_timer_create(apt_timer_queue_t *timer_queue, apt_timer_proc_f proc, void *obj, apr_pool_t *pool)
138 {
139 	apt_timer_t *timer = apr_palloc(pool,sizeof(apt_timer_t));
140 	APR_RING_ELEM_INIT(timer,link);
141 	timer->queue = timer_queue;
142 	timer->scheduled_time = 0;
143 	timer->proc = proc;
144 	timer->obj = obj;
145 	return timer;
146 }
147 
148 /** Set one-shot timer */
apt_timer_set(apt_timer_t * timer,apr_uint32_t timeout)149 APT_DECLARE(apt_bool_t) apt_timer_set(apt_timer_t *timer, apr_uint32_t timeout)
150 
151 {
152 	apt_timer_queue_t *queue = timer->queue;
153 
154 	if(timeout <= 0 || !timer->proc) {
155 		return FALSE;
156 	}
157 
158 	if(timer->scheduled_time) {
159 		/* remove timer first */
160 		apt_timer_remove(queue,timer);
161 	}
162 
163 	/* calculate time to elapse */
164 	timer->scheduled_time = queue->elapsed_time + timeout;
165 #ifdef APT_TIMER_DEBUG
166 	apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Set Timer 0x%x [%u]",timer,timer->scheduled_time);
167 #endif
168 	if(APR_RING_EMPTY(&queue->head, apt_timer_t, link)) {
169 		APR_RING_INSERT_TAIL(&queue->head,timer,apt_timer_t,link);
170 		return TRUE;
171 	}
172 
173 	/* insert new node (timer) to sorted by scheduled time list */
174 	return apt_timer_insert(queue,timer);
175 }
176 
177 /** Kill timer */
apt_timer_kill(apt_timer_t * timer)178 APT_DECLARE(apt_bool_t) apt_timer_kill(apt_timer_t *timer)
179 {
180 	if(!timer->scheduled_time) {
181 		return FALSE;
182 	}
183 
184 #ifdef APT_TIMER_DEBUG
185 	apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Kill Timer 0x%x [%u]",timer,timer->scheduled_time);
186 #endif
187 	return apt_timer_remove(timer->queue,timer);
188 }
189 
apt_timer_insert(apt_timer_queue_t * timer_queue,apt_timer_t * timer)190 static apt_bool_t apt_timer_insert(apt_timer_queue_t *timer_queue, apt_timer_t *timer)
191 {
192 	apt_timer_t *it;
193 	for(it = APR_RING_LAST(&timer_queue->head);
194 			it != APR_RING_SENTINEL(&timer_queue->head, apt_timer_t, link);
195 				it = APR_RING_PREV(it, link)) {
196 
197 		if(it->scheduled_time <= timer->scheduled_time) {
198 			APR_RING_INSERT_AFTER(it,timer,link);
199 			return TRUE;
200 		}
201 	}
202 	APR_RING_INSERT_HEAD(&timer_queue->head,timer,apt_timer_t,link);
203 	return TRUE;
204 }
205 
apt_timer_remove(apt_timer_queue_t * timer_queue,apt_timer_t * timer)206 static apt_bool_t apt_timer_remove(apt_timer_queue_t *timer_queue, apt_timer_t *timer)
207 {
208 	/* remove node (timer) from the list */
209 	APR_RING_REMOVE(timer,link);
210 	timer->scheduled_time = 0;
211 
212 	if(APR_RING_EMPTY(&timer_queue->head, apt_timer_t, link)) {
213 		/* reset elapsed time if no timers set */
214 		timer_queue->elapsed_time = 0;
215 	}
216 	return TRUE;
217 }
218 
apt_timers_reschedule(apt_timer_queue_t * timer_queue)219 static void apt_timers_reschedule(apt_timer_queue_t *timer_queue)
220 {
221 	apt_timer_t *it;
222 	for(it = APR_RING_LAST(&timer_queue->head);
223 			it != APR_RING_SENTINEL(&timer_queue->head, apt_timer_t, link);
224 				it = APR_RING_PREV(it, link)) {
225 
226 		it->scheduled_time -= timer_queue->elapsed_time;
227 	}
228 	timer_queue->elapsed_time = 0;
229 }
230