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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_atomic.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 mtime_t value, interval;
53 static void *vlc_timer_thread (void *data)
55 struct vlc_timer *timer = data;
57 vlc_mutex_lock (&timer->lock);
58 mutex_cleanup_push (&timer->lock);
62 while (timer->value == 0)
63 vlc_cond_wait (&timer->reschedule, &timer->lock);
65 if (vlc_cond_timedwait (&timer->reschedule, &timer->lock,
68 if (timer->interval == 0)
69 timer->value = 0; /* disarm */
70 vlc_mutex_unlock (&timer->lock);
72 int canc = vlc_savecancel ();
73 timer->func (timer->data);
74 vlc_restorecancel (canc);
76 mtime_t now = mdate ();
79 vlc_mutex_lock (&timer->lock);
80 if (timer->interval == 0)
83 misses = (now - timer->value) / timer->interval;
84 timer->value += timer->interval;
85 /* Try to compensate for one miss (mwait() will return immediately)
86 * but no more. Otherwise, we might busy loop, after extended periods
87 * without scheduling (suspend, SIGSTOP, RT preemption, ...). */
91 timer->value += misses * timer->interval;
92 atomic_fetch_add_explicit (&timer->overruns, misses,
93 memory_order_relaxed);
102 * Initializes an asynchronous timer.
103 * @warning Asynchronous timers are processed from an unspecified thread.
104 * Multiple occurences of a single interval timer are serialized; they cannot
107 * @param id pointer to timer to be initialized
108 * @param func function that the timer will call
109 * @param data parameter for the timer function
110 * @return 0 on success, a system error code otherwise.
112 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
114 struct vlc_timer *timer = malloc (sizeof (*timer));
116 if (unlikely(timer == NULL))
118 vlc_mutex_init (&timer->lock);
119 vlc_cond_init (&timer->reschedule);
125 atomic_init(&timer->overruns, 0);
127 if (vlc_clone (&timer->thread, vlc_timer_thread, timer,
128 VLC_THREAD_PRIORITY_INPUT))
130 vlc_cond_destroy (&timer->reschedule);
131 vlc_mutex_destroy (&timer->lock);
141 * Destroys an initialized timer. If needed, the timer is first disarmed.
142 * This function is undefined if the specified timer is not initialized.
144 * @warning This function <b>must</b> be called before the timer data can be
145 * freed and before the timer callback function can be unloaded.
147 * @param timer timer to destroy
149 void vlc_timer_destroy (vlc_timer_t timer)
151 vlc_cancel (timer->thread);
152 vlc_join (timer->thread, NULL);
153 vlc_cond_destroy (&timer->reschedule);
154 vlc_mutex_destroy (&timer->lock);
159 * Arm or disarm an initialized timer.
160 * This functions overrides any previous call to itself.
162 * @note A timer can fire later than requested due to system scheduling
163 * limitations. An interval timer can fail to trigger sometimes, either because
164 * the system is busy or suspended, or because a previous iteration of the
165 * timer is still running. See also vlc_timer_getoverrun().
167 * @param timer initialized timer
168 * @param absolute the timer value origin is the same as mdate() if true,
169 * the timer value is relative to now if false.
170 * @param value zero to disarm the timer, otherwise the initial time to wait
171 * before firing the timer.
172 * @param interval zero to fire the timer just once, otherwise the timer
173 * repetition interval.
175 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
176 mtime_t value, mtime_t interval)
178 if (!absolute && value != 0)
181 vlc_mutex_lock (&timer->lock);
182 timer->value = value;
183 timer->interval = interval;
184 vlc_cond_signal (&timer->reschedule);
185 vlc_mutex_unlock (&timer->lock);
189 * Fetch and reset the overrun counter for a timer.
190 * @param timer initialized timer
191 * @return the timer overrun counter, i.e. the number of times that the timer
192 * should have run but did not since the last actual run. If all is well, this
195 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
197 return atomic_exchange_explicit (&timer->overruns, 0,
198 memory_order_relaxed);