SDL-1.2.14
[sdl_omap.git] / src / timer / SDL_timer.c
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
31 int SDL_timer_started = 0;
32 int SDL_timer_running = 0;
33
34 /* Data to handle a single periodic alarm */
35 Uint32 SDL_alarm_interval = 0;
36 SDL_TimerCallback SDL_alarm_callback;
37
38 /* Data used for a thread-based timer */
39 static int SDL_timer_threaded = 0;
40
41 struct _SDL_TimerID {
42         Uint32 interval;
43         SDL_NewTimerCallback cb;
44         void *param;
45         Uint32 last_alarm;
46         struct _SDL_TimerID *next;
47 };
48
49 static SDL_TimerID SDL_timers = NULL;
50 static SDL_mutex *SDL_timer_mutex;
51 static 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 */
56 int 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
70 int 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
90 void 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
104 void 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
165 static 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
185 SDL_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
206 SDL_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 */
236 static Uint32 SDLCALL callback_wrapper(Uint32 ms, void *param)
237 {
238         SDL_TimerCallback func = (SDL_TimerCallback) param;
239         return (*func)(ms);
240 }
241
242 int 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 }