1 /*****************************************************************************
2 * timer.c: simple threaded timer
3 *****************************************************************************
4 * Copyright (C) 2009-2012 RĂ©mi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include <stdatomic.h>
30 #include <vlc_common.h>
33 * POSIX timers are essentially unusable from a library: there provide no safe
34 * way to ensure that a timer has no pending/ongoing iteration. Furthermore,
35 * they typically require one thread per timer plus one thread per iteration,
36 * which is inefficient and overkill (unless you need multiple iteration
37 * of the same timer concurrently).
38 * Thus, this is a generic manual implementation of timers using a thread.
44 vlc_cond_t reschedule
;
46 void (*func
) (void *);
48 vlc_tick_t value
, interval
;
53 static void *vlc_timer_thread (void *data
)
55 struct vlc_timer
*timer
= data
;
57 vlc_mutex_lock (&timer
->lock
);
61 if (timer
->value
== 0)
63 assert(timer
->interval
== 0);
64 vlc_cond_wait (&timer
->reschedule
, &timer
->lock
);
68 if (timer
->interval
!= 0)
70 vlc_tick_t now
= vlc_tick_now();
72 if (now
> timer
->value
)
73 { /* Update overrun counter */
74 unsigned misses
= (now
- timer
->value
) / timer
->interval
;
76 timer
->value
+= misses
* timer
->interval
;
77 assert(timer
->value
<= now
);
78 atomic_fetch_add_explicit(&timer
->overruns
, misses
,
79 memory_order_relaxed
);
83 vlc_tick_t value
= timer
->value
;
85 if (vlc_cond_timedwait(&timer
->reschedule
, &timer
->lock
, value
) == 0)
88 if (likely(timer
->value
<= value
))
90 timer
->value
+= timer
->interval
; /* rearm */
92 if (timer
->interval
== 0)
93 timer
->value
= 0; /* disarm */
96 vlc_mutex_unlock (&timer
->lock
);
97 timer
->func (timer
->data
);
98 vlc_mutex_lock (&timer
->lock
);
101 vlc_mutex_unlock (&timer
->lock
);
105 int vlc_timer_create (vlc_timer_t
*id
, void (*func
) (void *), void *data
)
107 struct vlc_timer
*timer
= malloc (sizeof (*timer
));
109 if (unlikely(timer
== NULL
))
111 vlc_mutex_init (&timer
->lock
);
112 vlc_cond_init (&timer
->reschedule
);
119 atomic_init(&timer
->overruns
, 0);
121 if (vlc_clone (&timer
->thread
, vlc_timer_thread
, timer
,
122 VLC_THREAD_PRIORITY_INPUT
))
132 void vlc_timer_destroy (vlc_timer_t timer
)
134 vlc_mutex_lock(&timer
->lock
);
136 vlc_cond_signal(&timer
->reschedule
);
137 vlc_mutex_unlock(&timer
->lock
);
139 vlc_join (timer
->thread
, NULL
);
143 void vlc_timer_schedule (vlc_timer_t timer
, bool absolute
,
144 vlc_tick_t value
, vlc_tick_t interval
)
146 if (value
== VLC_TIMER_DISARM
)
150 value
+= vlc_tick_now();
152 vlc_mutex_lock (&timer
->lock
);
153 timer
->value
= value
;
154 timer
->interval
= interval
;
155 vlc_cond_signal (&timer
->reschedule
);
156 vlc_mutex_unlock (&timer
->lock
);
159 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
161 return atomic_exchange_explicit (&timer
->overruns
, 0,
162 memory_order_relaxed
);