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