SDL-1.2.14
[sdl_omap.git] / src / timer / SDL_timer.c
CommitLineData
e14743d1 1/*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#include "SDL_timer.h"
25#include "SDL_timer_c.h"
26#include "SDL_mutex.h"
27#include "SDL_systimer.h"
28
29/* #define DEBUG_TIMERS */
30
31int SDL_timer_started = 0;
32int SDL_timer_running = 0;
33
34/* Data to handle a single periodic alarm */
35Uint32 SDL_alarm_interval = 0;
36SDL_TimerCallback SDL_alarm_callback;
37
38/* Data used for a thread-based timer */
39static int SDL_timer_threaded = 0;
40
41struct _SDL_TimerID {
42 Uint32 interval;
43 SDL_NewTimerCallback cb;
44 void *param;
45 Uint32 last_alarm;
46 struct _SDL_TimerID *next;
47};
48
49static SDL_TimerID SDL_timers = NULL;
50static SDL_mutex *SDL_timer_mutex;
51static volatile SDL_bool list_changed = SDL_FALSE;
52
53/* Set whether or not the timer should use a thread.
54 This should not be called while the timer subsystem is running.
55*/
56int SDL_SetTimerThreaded(int value)
57{
58 int retval;
59
60 if ( SDL_timer_started ) {
61 SDL_SetError("Timer already initialized");
62 retval = -1;
63 } else {
64 retval = 0;
65 SDL_timer_threaded = value;
66 }
67 return retval;
68}
69
70int SDL_TimerInit(void)
71{
72 int retval;
73
74 retval = 0;
75 if ( SDL_timer_started ) {
76 SDL_TimerQuit();
77 }
78 if ( ! SDL_timer_threaded ) {
79 retval = SDL_SYS_TimerInit();
80 }
81 if ( SDL_timer_threaded ) {
82 SDL_timer_mutex = SDL_CreateMutex();
83 }
84 if ( retval == 0 ) {
85 SDL_timer_started = 1;
86 }
87 return(retval);
88}
89
90void SDL_TimerQuit(void)
91{
92 SDL_SetTimer(0, NULL);
93 if ( SDL_timer_threaded < 2 ) {
94 SDL_SYS_TimerQuit();
95 }
96 if ( SDL_timer_threaded ) {
97 SDL_DestroyMutex(SDL_timer_mutex);
98 SDL_timer_mutex = NULL;
99 }
100 SDL_timer_started = 0;
101 SDL_timer_threaded = 0;
102}
103
104void SDL_ThreadedTimerCheck(void)
105{
106 Uint32 now, ms;
107 SDL_TimerID t, prev, next;
108 SDL_bool removed;
109
110 SDL_mutexP(SDL_timer_mutex);
111 list_changed = SDL_FALSE;
112 now = SDL_GetTicks();
113 for ( prev = NULL, t = SDL_timers; t; t = next ) {
114 removed = SDL_FALSE;
115 ms = t->interval - SDL_TIMESLICE;
116 next = t->next;
117 if ( (int)(now - t->last_alarm) > (int)ms ) {
118 struct _SDL_TimerID timer;
119
120 if ( (now - t->last_alarm) < t->interval ) {
121 t->last_alarm += t->interval;
122 } else {
123 t->last_alarm = now;
124 }
125#ifdef DEBUG_TIMERS
126 printf("Executing timer %p (thread = %d)\n",
127 t, SDL_ThreadID());
128#endif
129 timer = *t;
130 SDL_mutexV(SDL_timer_mutex);
131 ms = timer.cb(timer.interval, timer.param);
132 SDL_mutexP(SDL_timer_mutex);
133 if ( list_changed ) {
134 /* Abort, list of timers modified */
135 /* FIXME: what if ms was changed? */
136 break;
137 }
138 if ( ms != t->interval ) {
139 if ( ms ) {
140 t->interval = ROUND_RESOLUTION(ms);
141 } else {
142 /* Remove timer from the list */
143#ifdef DEBUG_TIMERS
144 printf("SDL: Removing timer %p\n", t);
145#endif
146 if ( prev ) {
147 prev->next = next;
148 } else {
149 SDL_timers = next;
150 }
151 SDL_free(t);
152 --SDL_timer_running;
153 removed = SDL_TRUE;
154 }
155 }
156 }
157 /* Don't update prev if the timer has disappeared */
158 if ( ! removed ) {
159 prev = t;
160 }
161 }
162 SDL_mutexV(SDL_timer_mutex);
163}
164
165static SDL_TimerID SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback, void *param)
166{
167 SDL_TimerID t;
168 t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
169 if ( t ) {
170 t->interval = ROUND_RESOLUTION(interval);
171 t->cb = callback;
172 t->param = param;
173 t->last_alarm = SDL_GetTicks();
174 t->next = SDL_timers;
175 SDL_timers = t;
176 ++SDL_timer_running;
177 list_changed = SDL_TRUE;
178 }
179#ifdef DEBUG_TIMERS
180 printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, SDL_timer_running);
181#endif
182 return t;
183}
184
185SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
186{
187 SDL_TimerID t;
188 if ( ! SDL_timer_mutex ) {
189 if ( SDL_timer_started ) {
190 SDL_SetError("This platform doesn't support multiple timers");
191 } else {
192 SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
193 }
194 return NULL;
195 }
196 if ( ! SDL_timer_threaded ) {
197 SDL_SetError("Multiple timers require threaded events!");
198 return NULL;
199 }
200 SDL_mutexP(SDL_timer_mutex);
201 t = SDL_AddTimerInternal(interval, callback, param);
202 SDL_mutexV(SDL_timer_mutex);
203 return t;
204}
205
206SDL_bool SDL_RemoveTimer(SDL_TimerID id)
207{
208 SDL_TimerID t, prev = NULL;
209 SDL_bool removed;
210
211 removed = SDL_FALSE;
212 SDL_mutexP(SDL_timer_mutex);
213 /* Look for id in the linked list of timers */
214 for (t = SDL_timers; t; prev=t, t = t->next ) {
215 if ( t == id ) {
216 if(prev) {
217 prev->next = t->next;
218 } else {
219 SDL_timers = t->next;
220 }
221 SDL_free(t);
222 --SDL_timer_running;
223 removed = SDL_TRUE;
224 list_changed = SDL_TRUE;
225 break;
226 }
227 }
228#ifdef DEBUG_TIMERS
229 printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID());
230#endif
231 SDL_mutexV(SDL_timer_mutex);
232 return removed;
233}
234
235/* Old style callback functions are wrapped through this */
236static Uint32 SDLCALL callback_wrapper(Uint32 ms, void *param)
237{
238 SDL_TimerCallback func = (SDL_TimerCallback) param;
239 return (*func)(ms);
240}
241
242int SDL_SetTimer(Uint32 ms, SDL_TimerCallback callback)
243{
244 int retval;
245
246#ifdef DEBUG_TIMERS
247 printf("SDL_SetTimer(%d)\n", ms);
248#endif
249 retval = 0;
250
251 if ( SDL_timer_threaded ) {
252 SDL_mutexP(SDL_timer_mutex);
253 }
254 if ( SDL_timer_running ) { /* Stop any currently running timer */
255 if ( SDL_timer_threaded ) {
256 while ( SDL_timers ) {
257 SDL_TimerID freeme = SDL_timers;
258 SDL_timers = SDL_timers->next;
259 SDL_free(freeme);
260 }
261 SDL_timer_running = 0;
262 list_changed = SDL_TRUE;
263 } else {
264 SDL_SYS_StopTimer();
265 SDL_timer_running = 0;
266 }
267 }
268 if ( ms ) {
269 if ( SDL_timer_threaded ) {
270 if ( SDL_AddTimerInternal(ms, callback_wrapper, (void *)callback) == NULL ) {
271 retval = -1;
272 }
273 } else {
274 SDL_timer_running = 1;
275 SDL_alarm_interval = ms;
276 SDL_alarm_callback = callback;
277 retval = SDL_SYS_StartTimer();
278 }
279 }
280 if ( SDL_timer_threaded ) {
281 SDL_mutexV(SDL_timer_mutex);
282 }
283
284 return retval;
285}