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 <stdnoreturn.h>
30 #include <vlc_common.h>
31 #include <vlc_atomic.h>
34 * POSIX timers are essentially unusable from a library: there provide no safe
35 * way to ensure that a timer has no pending/ongoing iteration. Furthermore,
36 * they typically require one thread per timer plus one thread per iteration,
37 * which is inefficient and overkill (unless you need multiple iteration
38 * of the same timer concurrently).
39 * Thus, this is a generic manual implementation of timers using a thread.
45 vlc_cond_t reschedule
;
47 void (*func
) (void *);
49 mtime_t value
, interval
;
53 noreturn
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)
64 assert(timer
->interval
== 0);
65 vlc_cond_wait (&timer
->reschedule
, &timer
->lock
);
68 if (timer
->interval
!= 0)
70 mtime_t now
= mdate();
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 mtime_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
);
98 int canc
= vlc_savecancel ();
99 timer
->func (timer
->data
);
100 vlc_restorecancel (canc
);
102 vlc_mutex_lock (&timer
->lock
);
106 vlc_assert_unreachable ();
109 int vlc_timer_create (vlc_timer_t
*id
, void (*func
) (void *), void *data
)
111 struct vlc_timer
*timer
= malloc (sizeof (*timer
));
113 if (unlikely(timer
== NULL
))
115 vlc_mutex_init (&timer
->lock
);
116 vlc_cond_init (&timer
->reschedule
);
122 atomic_init(&timer
->overruns
, 0);
124 if (vlc_clone (&timer
->thread
, vlc_timer_thread
, timer
,
125 VLC_THREAD_PRIORITY_INPUT
))
127 vlc_cond_destroy (&timer
->reschedule
);
128 vlc_mutex_destroy (&timer
->lock
);
137 void vlc_timer_destroy (vlc_timer_t timer
)
139 vlc_cancel (timer
->thread
);
140 vlc_join (timer
->thread
, NULL
);
141 vlc_cond_destroy (&timer
->reschedule
);
142 vlc_mutex_destroy (&timer
->lock
);
146 void vlc_timer_schedule (vlc_timer_t timer
, bool absolute
,
147 mtime_t value
, mtime_t interval
)
155 vlc_mutex_lock (&timer
->lock
);
156 timer
->value
= value
;
157 timer
->interval
= interval
;
158 vlc_cond_signal (&timer
->reschedule
);
159 vlc_mutex_unlock (&timer
->lock
);
162 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
164 return atomic_exchange_explicit (&timer
->overruns
, 0,
165 memory_order_relaxed
);