libamxp  1.4.0
Patterns C Implementation
amxp_timer.c
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** SPDX-License-Identifier: BSD-2-Clause-Patent
4 **
5 ** SPDX-FileCopyrightText: Copyright (c) 2023 SoftAtHome
6 **
7 ** Redistribution and use in source and binary forms, with or without modification,
8 ** are permitted provided that the following conditions are met:
9 **
10 ** 1. Redistributions of source code must retain the above copyright notice,
11 ** this list of conditions and the following disclaimer.
12 **
13 ** 2. Redistributions in binary form must reproduce the above copyright notice,
14 ** this list of conditions and the following disclaimer in the documentation
15 ** and/or other materials provided with the distribution.
16 **
17 ** Subject to the terms and conditions of this license, each copyright holder
18 ** and contributor hereby grants to those receiving rights under this license
19 ** a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
20 ** (except for failure to satisfy the conditions of this license) patent license
21 ** to make, have made, use, offer to sell, sell, import, and otherwise transfer
22 ** this software, where such license applies only to those patent claims, already
23 ** acquired or hereafter acquired, licensable by such copyright holder or contributor
24 ** that are necessarily infringed by:
25 **
26 ** (a) their Contribution(s) (the licensed copyrights of copyright holders and
27 ** non-copyrightable additions of contributors, in source or binary form) alone;
28 ** or
29 **
30 ** (b) combination of their Contribution(s) with the work of authorship to which
31 ** such Contribution(s) was added by such copyright holder or contributor, if,
32 ** at the time the Contribution is added, such addition causes such combination
33 ** to be necessarily infringed. The patent license shall not apply to any other
34 ** combinations which include the Contribution.
35 **
36 ** Except as expressly stated above, no rights or licenses from any copyright
37 ** holder or contributor is granted under this license, whether expressly, by
38 ** implication, estoppel or otherwise.
39 **
40 ** DISCLAIMER
41 **
42 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
46 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
51 ** USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 **
53 ****************************************************************************/
54 
55 #ifndef _GNU_SOURCE
56 #define _GNU_SOURCE
57 #endif
58 
59 #include <stdlib.h>
60 
61 #include <amxc/amxc.h>
62 #include <amxc/amxc_macros.h>
63 #include <amxp/amxp.h>
64 
65 static amxc_llist_t timers;
66 static struct timeval current;
67 static bool timers_enabled = true;
68 
69 static void amxp_timer_free_it(amxc_llist_it_t* it) {
70  amxp_timer_t* timer = amxc_llist_it_get_data(it, amxp_timer_t, it);
71  free(timer);
72 }
73 
74 static struct timeval amxp_timer_get_elapsed_time(void) {
75  struct itimerval ti;
76  struct timeval elapsed = { 0, 0 };
77 
78  /* get the elapsed period */
79  if(!timerisset(&current)) {
80  return elapsed;
81  }
82 
83  getitimer(ITIMER_REAL, &ti);
84  if(timerisset(&ti.it_value)) {
85  timersub(&current, &ti.it_value, &elapsed);
86  } else {
87  elapsed = current;
88  }
89 
90  return elapsed;
91 }
92 
93 static void amxp_timer_update_current(struct timeval* elapsed) {
94  timersub(&current, elapsed, &current);
95 
96  if(current.tv_sec < 0) {
97  timerclear(&current);
98  }
99 }
100 
101 static void amxp_timer_start_timer(amxp_timer_t* timer, struct timeval* smallest) {
102  timer->state = amxp_timer_running;
103  // *INDENT-OFF*
104  if(!timerisset(smallest) || timercmp(smallest, &timer->timer.it_value, >)) {
105  (*smallest) = timer->timer.it_value;
106  }
107  // *INDENT-ON*
108 }
109 
110 static bool amxp_timer_update(amxp_timer_t* timer,
111  struct timeval* smallest,
112  struct timeval* elapsed) {
113  timersub(&timer->timer.it_value, elapsed, &timer->timer.it_value);
114  if(!((timer->timer.it_value.tv_sec < 0) ||
115  !timerisset(&timer->timer.it_value))) {
116 
117  // *INDENT-OFF*
118  if(!timerisset(smallest) ||
119  timercmp(smallest, &timer->timer.it_value, >)) {
120  (*smallest) = timer->timer.it_value;
121  }
122  // *INDENT-ON*
123  return true;
124  }
125 
126  if(timer->state != amxp_timer_expired) {
127  timer->state = amxp_timer_expired;
128  }
129 
130  if(timerisset(&timer->timer.it_interval)) {
131  timer->timer.it_value = timer->timer.it_interval;
132  // *INDENT-OFF*
133  if(!timerisset(smallest) ||
134  timercmp(smallest, &timer->timer.it_value, >)) {
135  (*smallest) = timer->timer.it_value;
136  }
137  // *INDENT-ON*
138  return true;
139  }
140 
141  return false;
142 }
143 
145  struct timeval elapsed = { 0, 0 };
146  bool active_timer = false;
147  struct itimerval ti;
148  struct timeval smallest;
149 
150  elapsed = amxp_timer_get_elapsed_time();
151 
152  timerclear(&smallest);
153  amxp_timer_update_current(&elapsed);
154 
155  amxc_llist_for_each(it, (&timers)) {
156  amxp_timer_t* timer = amxc_llist_it_get_data(it, amxp_timer_t, it);
157  if(timer->state == amxp_timer_deleted) {
158  continue;
159  }
160  if(timer->state == amxp_timer_off) {
161  continue;
162  }
163  if(timer->state == amxp_timer_started) {
164  amxp_timer_start_timer(timer, &smallest);
165  active_timer = true;
166  continue;
167  }
168 
169  active_timer |= amxp_timer_update(timer, &smallest, &elapsed);
170  }
171 
172  timerclear(&ti.it_interval);
173  current = smallest;
174  if(active_timer) {
175  ti.it_value = current;
176  } else {
177  ti.it_value.tv_sec = 0;
178  ti.it_value.tv_usec = 0;
179  }
180  setitimer(ITIMER_REAL, &ti, NULL);
181 }
182 
183 void amxp_timers_check(void) {
184  static uint32_t recursive = 0;
185  amxp_timer_t* timer = NULL;
186  amxc_llist_it_t* it = NULL;
187 
188  recursive++;
189  when_true(!timers_enabled, exit);
190 
191  it = amxc_llist_get_first(&timers);
192  // do not use amxc_llist_for_each here.
193  // it is possible that in the callback function a new timer is added.
194  // this new timer must be taken into account as well.
195  // new timers are always added to the end of the list.
196  while(it) {
197  timer = amxc_llist_it_get_data(it, amxp_timer_t, it);
198  if((timer->state == amxp_timer_deleted) && (recursive == 1)) {
199  it = amxc_llist_it_get_next(&timer->it);
200  amxc_llist_it_clean(&timer->it, amxp_timer_free_it);
201  continue;
202  }
203 
204  if(timer->state == amxp_timer_expired) {
205  if(timerisset(&timer->timer.it_interval)) {
206  timer->state = amxp_timer_running;
207  } else {
208  timer->state = amxp_timer_off;
209  }
210  if(timer->cb) {
211  amxp_timers_enable(false);
212  timer->cb(timer, timer->priv);
213  amxp_timers_enable(true);
214  }
215  }
216 
217  it = amxc_llist_it_get_next(&timer->it);
218  }
219 
220 exit:
221  recursive--;
222  return;
223 }
224 
225 void amxp_timers_enable(bool enable) {
226  timers_enabled = enable;
227 }
228 
229 int amxp_timer_new(amxp_timer_t** timer, amxp_timer_cb_t cb, void* priv) {
230  int retval = -1;
231 
232  when_null(timer, exit);
233  when_not_null(*timer, exit);
234 
235  *timer = (amxp_timer_t*) calloc(1, sizeof(amxp_timer_t));
236  when_null(*timer, exit);
237  (*timer)->cb = cb;
238  (*timer)->priv = priv;
239  amxc_llist_append(&timers, &(*timer)->it);
240 
241  retval = 0;
242 
243 exit:
244  return retval;
245 }
246 
248  when_null(timer, exit);
249  when_null(*timer, exit);
250 
251  amxp_timer_stop(*timer);
252  (*timer)->state = amxp_timer_deleted;
253  *timer = NULL;
254 
255 exit:
256  return;
257 }
258 
259 int amxp_timer_set_interval(amxp_timer_t* timer, unsigned int msec) {
260  int retval = -1;
261 
262  when_null(timer, exit);
263 
264  if(msec == 0) {
265  // this is the absolute minimum value for a timer.
266  // if the value is 0 setitimer has no effect as when set to zero
267  // the timer is stopped (and will never trigger SIGALRM)
268  // see man page of setitimer
269  msec = 1;
270  }
271 
272  timer->timer.it_interval.tv_sec = msec / 1000;
273  timer->timer.it_interval.tv_usec = (msec % 1000) * 1000;
274 
275  retval = 0;
276 
277 exit:
278  return retval;
279 }
280 
282  unsigned int retval = 0;
283 
284  when_null(timer, exit);
285  when_true(timer->state != amxp_timer_running &&
286  timer->state != amxp_timer_started,
287  exit);
288 
289  retval = timer->timer.it_value.tv_sec * 1000 +
290  timer->timer.it_value.tv_usec / 1000;
291 
292 exit:
293  return retval;
294 }
295 
296 int amxp_timer_start(amxp_timer_t* timer, unsigned int timeout_msec) {
297  int retval = -1;
298  when_null(timer, exit);
299  when_true(timer->state == amxp_timer_deleted, exit);
300 
301  if(timeout_msec == 0) {
302  // this is the absolute minimum value for a timer.
303  // if the value is 0 setitimer has no effect as when set to zero
304  // the timer is stopped (and will never trigger SIGALRM)
305  // see man page of setitimer
306  timeout_msec = 1;
307  }
308  timer->timer.it_value.tv_sec = timeout_msec / 1000;
309  timer->timer.it_value.tv_usec = (timeout_msec % 1000) * 1000;
310  timer->state = amxp_timer_started;
311 
313 
314  retval = 0;
315 exit:
316  return retval;
317 }
318 
320  int retval = -1;
321  when_null(timer, exit);
322  when_true(timer->state == amxp_timer_deleted, exit);
323 
324  timer->state = amxp_timer_off;
325 
327 
328  retval = 0;
329 
330 exit:
331  return retval;
332 }
333 
335  return timer == NULL ? amxp_timer_off : timer->state;
336 }
337 
338 CONSTRUCTOR_LVL(101) static void amxp_timers_init(void) {
339  amxc_llist_init(&timers);
340 }
341 
342 DESTRUCTOR_LVL(101) static void amxp_timers_cleanup(void) {
343  amxc_llist_clean(&timers, amxp_timer_free_it);
344 }
static struct timeval current
Definition: amxp_timer.c:66
static amxc_llist_t timers
Definition: amxp_timer.c:65
static void amxp_timer_free_it(amxc_llist_it_t *it)
Definition: amxp_timer.c:69
DESTRUCTOR_LVL(101)
Definition: amxp_timer.c:342
static struct timeval amxp_timer_get_elapsed_time(void)
Definition: amxp_timer.c:74
static void amxp_timer_update_current(struct timeval *elapsed)
Definition: amxp_timer.c:93
CONSTRUCTOR_LVL(101)
Definition: amxp_timer.c:338
static bool amxp_timer_update(amxp_timer_t *timer, struct timeval *smallest, struct timeval *elapsed)
Definition: amxp_timer.c:110
static bool timers_enabled
Definition: amxp_timer.c:67
static void amxp_timer_start_timer(amxp_timer_t *timer, struct timeval *smallest)
Definition: amxp_timer.c:101
int amxp_timer_start(amxp_timer_t *timer, unsigned int timeout_msec)
Starts or resets a timer.
Definition: amxp_timer.c:296
unsigned int amxp_timer_remaining_time(amxp_timer_t *timer)
Get the remaining time of the timer.
Definition: amxp_timer.c:281
amxp_timer_state_t amxp_timer_get_state(amxp_timer_t *timer)
Get the timer's state.
Definition: amxp_timer.c:334
void amxp_timers_check(void)
Check all timers and call the callback function when the timer is in Timer expired state.
Definition: amxp_timer.c:183
void(* amxp_timer_cb_t)(amxp_timer_t *timer, void *priv)
Timer timeout callback function.
Definition: amxp_timer.h:139
int amxp_timer_stop(amxp_timer_t *timer)
Stops the timer.
Definition: amxp_timer.c:319
enum _amxp_timer_state amxp_timer_state_t
The timer states.
void amxp_timers_calculate(void)
Caclulates the remaining time of all timers.
Definition: amxp_timer.c:144
void amxp_timers_enable(bool enable)
Enable or disable all timers.
Definition: amxp_timer.c:225
void amxp_timer_delete(amxp_timer_t **timer)
Deletes a timer.
Definition: amxp_timer.c:247
int amxp_timer_new(amxp_timer_t **timer, amxp_timer_cb_t cb, void *priv)
Allocate and initializes a new timer.
Definition: amxp_timer.c:229
int amxp_timer_set_interval(amxp_timer_t *timer, unsigned int msec)
Sets the interval of a timer in milli seconds.
Definition: amxp_timer.c:259
@ amxp_timer_off
Definition: amxp_timer.h:149
@ amxp_timer_running
Definition: amxp_timer.h:151
@ amxp_timer_expired
Definition: amxp_timer.h:152
@ amxp_timer_deleted
Definition: amxp_timer.h:153
@ amxp_timer_started
Definition: amxp_timer.h:150
The timer type.
Definition: amxp_timer.h:163
struct itimerval timer
Definition: amxp_timer.h:165
void * priv
Definition: amxp_timer.h:168
amxp_timer_state_t state
Definition: amxp_timer.h:166
amxc_llist_it_t it
Definition: amxp_timer.h:164
amxp_timer_cb_t cb
Definition: amxp_timer.h:167