SDL
2.0
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
File List
Globals
SDL_timer.c
Go to the documentation of this file.
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "../SDL_internal.h"
22
23
#include "
SDL_timer.h
"
24
#include "
SDL_timer_c.h
"
25
#include "
SDL_atomic.h
"
26
#include "
SDL_cpuinfo.h
"
27
#include "../thread/SDL_systhread.h"
28
29
/* #define DEBUG_TIMERS */
30
31
typedef
struct
_SDL_Timer
32
{
33
int
timerID
;
34
SDL_TimerCallback
callback
;
35
void
*
param
;
36
Uint32
interval
;
37
Uint32
scheduled
;
38
SDL_atomic_t
canceled
;
39
struct
_SDL_Timer *
next
;
40
}
SDL_Timer
;
41
42
typedef
struct
_SDL_TimerMap
43
{
44
int
timerID
;
45
SDL_Timer
*
timer
;
46
struct
_SDL_TimerMap *
next
;
47
}
SDL_TimerMap
;
48
49
/* The timers are kept in a sorted list */
50
typedef
struct
{
51
/* Data used by the main thread */
52
SDL_Thread
*
thread
;
53
SDL_atomic_t
nextID
;
54
SDL_TimerMap
*
timermap
;
55
SDL_mutex
*
timermap_lock
;
56
57
/* Padding to separate cache lines between threads */
58
char
cache_pad[
SDL_CACHELINE_SIZE
];
59
60
/* Data used to communicate with the timer thread */
61
SDL_SpinLock
lock
;
62
SDL_sem *
sem
;
63
SDL_Timer
*
pending
;
64
SDL_Timer
*
freelist
;
65
SDL_atomic_t
active
;
66
67
/* List of timers - this is only touched by the timer thread */
68
SDL_Timer
*
timers
;
69
}
SDL_TimerData
;
70
71
static
SDL_TimerData
SDL_timer_data
;
72
73
/* The idea here is that any thread might add a timer, but a single
74
* thread manages the active timer queue, sorted by scheduling time.
75
*
76
* Timers are removed by simply setting a canceled flag
77
*/
78
79
static
void
80
SDL_AddTimerInternal
(
SDL_TimerData
*
data
,
SDL_Timer
*timer)
81
{
82
SDL_Timer
*prev, *curr;
83
84
prev =
NULL
;
85
for
(curr = data->
timers
; curr; prev = curr, curr = curr->
next
) {
86
if
((
Sint32
)(timer->
scheduled
-curr->
scheduled
) < 0) {
87
break
;
88
}
89
}
90
91
/* Insert the timer here! */
92
if
(prev) {
93
prev->
next
= timer;
94
}
else
{
95
data->
timers
= timer;
96
}
97
timer->
next
= curr;
98
}
99
100
static
int
SDLCALL
101
SDL_TimerThread
(
void
*_data)
102
{
103
SDL_TimerData
*
data
= (
SDL_TimerData
*)_data;
104
SDL_Timer
*pending;
105
SDL_Timer
*current;
106
SDL_Timer
*freelist_head =
NULL
;
107
SDL_Timer
*freelist_tail =
NULL
;
108
Uint32
tick, now, interval, delay;
109
110
/* Threaded timer loop:
111
* 1. Queue timers added by other threads
112
* 2. Handle any timers that should dispatch this cycle
113
* 3. Wait until next dispatch time or new timer arrives
114
*/
115
for
( ; ; ) {
116
/* Pending and freelist maintenance */
117
SDL_AtomicLock
(&data->
lock
);
118
{
119
/* Get any timers ready to be queued */
120
pending = data->
pending
;
121
data->
pending
=
NULL
;
122
123
/* Make any unused timer structures available */
124
if
(freelist_head) {
125
freelist_tail->
next
= data->
freelist
;
126
data->
freelist
= freelist_head;
127
}
128
}
129
SDL_AtomicUnlock
(&data->
lock
);
130
131
/* Sort the pending timers into our list */
132
while
(pending) {
133
current = pending;
134
pending = pending->
next
;
135
SDL_AddTimerInternal
(data, current);
136
}
137
freelist_head =
NULL
;
138
freelist_tail =
NULL
;
139
140
/* Check to see if we're still running, after maintenance */
141
if
(!
SDL_AtomicGet
(&data->
active
)) {
142
break
;
143
}
144
145
/* Initial delay if there are no timers */
146
delay =
SDL_MUTEX_MAXWAIT
;
147
148
tick =
SDL_GetTicks
();
149
150
/* Process all the pending timers for this tick */
151
while
(data->
timers
) {
152
current = data->
timers
;
153
154
if
((
Sint32
)(tick-current->
scheduled
) < 0) {
155
/* Scheduled for the future, wait a bit */
156
delay = (current->
scheduled
- tick);
157
break
;
158
}
159
160
/* We're going to do something with this timer */
161
data->
timers
= current->
next
;
162
163
if
(
SDL_AtomicGet
(¤t->
canceled
)) {
164
interval = 0;
165
}
else
{
166
interval = current->
callback
(current->
interval
, current->
param
);
167
}
168
169
if
(interval > 0) {
170
/* Reschedule this timer */
171
current->
interval
= interval;
172
current->
scheduled
= tick + interval;
173
SDL_AddTimerInternal
(data, current);
174
}
else
{
175
if
(!freelist_head) {
176
freelist_head = current;
177
}
178
if
(freelist_tail) {
179
freelist_tail->
next
= current;
180
}
181
freelist_tail = current;
182
183
SDL_AtomicSet
(¤t->
canceled
, 1);
184
}
185
}
186
187
/* Adjust the delay based on processing time */
188
now =
SDL_GetTicks
();
189
interval = (now - tick);
190
if
(interval > delay) {
191
delay = 0;
192
}
else
{
193
delay -= interval;
194
}
195
196
/* Note that each time a timer is added, this will return
197
immediately, but we process the timers added all at once.
198
That's okay, it just means we run through the loop a few
199
extra times.
200
*/
201
SDL_SemWaitTimeout
(data->
sem
, delay);
202
}
203
return
0;
204
}
205
206
int
207
SDL_TimerInit
(
void
)
208
{
209
SDL_TimerData
*
data
= &
SDL_timer_data
;
210
211
if
(!
SDL_AtomicGet
(&data->
active
)) {
212
const
char
*
name
=
"SDLTimer"
;
213
data->
timermap_lock
=
SDL_CreateMutex
();
214
if
(!data->
timermap_lock
) {
215
return
-1;
216
}
217
218
data->
sem
=
SDL_CreateSemaphore
(0);
219
if
(!data->
sem
) {
220
SDL_DestroyMutex
(data->
timermap_lock
);
221
return
-1;
222
}
223
224
SDL_AtomicSet
(&data->
active
, 1);
225
226
/* Timer threads use a callback into the app, so we can't set a limited stack size here. */
227
data->
thread
=
SDL_CreateThreadInternal
(
SDL_TimerThread
, name, 0, data);
228
if
(!data->
thread
) {
229
SDL_TimerQuit
();
230
return
-1;
231
}
232
233
SDL_AtomicSet
(&data->
nextID
, 1);
234
}
235
return
0;
236
}
237
238
void
239
SDL_TimerQuit
(
void
)
240
{
241
SDL_TimerData
*
data
= &
SDL_timer_data
;
242
SDL_Timer
*timer;
243
SDL_TimerMap
*entry;
244
245
if
(
SDL_AtomicCAS
(&data->
active
, 1, 0)) {
/* active? Move to inactive. */
246
/* Shutdown the timer thread */
247
if
(data->
thread
) {
248
SDL_SemPost
(data->
sem
);
249
SDL_WaitThread
(data->
thread
,
NULL
);
250
data->
thread
=
NULL
;
251
}
252
253
SDL_DestroySemaphore
(data->
sem
);
254
data->
sem
=
NULL
;
255
256
/* Clean up the timer entries */
257
while
(data->
timers
) {
258
timer = data->
timers
;
259
data->
timers
= timer->
next
;
260
SDL_free
(timer);
261
}
262
while
(data->
freelist
) {
263
timer = data->
freelist
;
264
data->
freelist
= timer->
next
;
265
SDL_free
(timer);
266
}
267
while
(data->
timermap
) {
268
entry = data->
timermap
;
269
data->
timermap
= entry->
next
;
270
SDL_free
(entry);
271
}
272
273
SDL_DestroyMutex
(data->
timermap_lock
);
274
data->
timermap_lock
=
NULL
;
275
}
276
}
277
278
SDL_TimerID
279
SDL_AddTimer
(
Uint32
interval,
SDL_TimerCallback
callback
,
void
*
param
)
280
{
281
SDL_TimerData
*
data
= &
SDL_timer_data
;
282
SDL_Timer
*timer;
283
SDL_TimerMap
*entry;
284
285
SDL_AtomicLock
(&data->
lock
);
286
if
(!
SDL_AtomicGet
(&data->
active
)) {
287
if
(
SDL_TimerInit
() < 0) {
288
SDL_AtomicUnlock
(&data->
lock
);
289
return
0;
290
}
291
}
292
293
timer = data->
freelist
;
294
if
(timer) {
295
data->
freelist
= timer->
next
;
296
}
297
SDL_AtomicUnlock
(&data->
lock
);
298
299
if
(timer) {
300
SDL_RemoveTimer
(timer->
timerID
);
301
}
else
{
302
timer = (
SDL_Timer
*)
SDL_malloc
(
sizeof
(*timer));
303
if
(!timer) {
304
SDL_OutOfMemory
();
305
return
0;
306
}
307
}
308
timer->
timerID
=
SDL_AtomicIncRef
(&data->
nextID
);
309
timer->
callback
=
callback
;
310
timer->
param
=
param
;
311
timer->
interval
= interval;
312
timer->
scheduled
=
SDL_GetTicks
() + interval;
313
SDL_AtomicSet
(&timer->
canceled
, 0);
314
315
entry = (
SDL_TimerMap
*)
SDL_malloc
(
sizeof
(*entry));
316
if
(!entry) {
317
SDL_free
(timer);
318
SDL_OutOfMemory
();
319
return
0;
320
}
321
entry->
timer
= timer;
322
entry->
timerID
= timer->
timerID
;
323
324
SDL_LockMutex
(data->
timermap_lock
);
325
entry->
next
= data->
timermap
;
326
data->
timermap
= entry;
327
SDL_UnlockMutex
(data->
timermap_lock
);
328
329
/* Add the timer to the pending list for the timer thread */
330
SDL_AtomicLock
(&data->
lock
);
331
timer->
next
= data->
pending
;
332
data->
pending
= timer;
333
SDL_AtomicUnlock
(&data->
lock
);
334
335
/* Wake up the timer thread if necessary */
336
SDL_SemPost
(data->
sem
);
337
338
return
entry->
timerID
;
339
}
340
341
SDL_bool
342
SDL_RemoveTimer
(
SDL_TimerID
id
)
343
{
344
SDL_TimerData
*
data
= &
SDL_timer_data
;
345
SDL_TimerMap
*prev, *entry;
346
SDL_bool
canceled =
SDL_FALSE
;
347
348
/* Find the timer */
349
SDL_LockMutex
(data->
timermap_lock
);
350
prev =
NULL
;
351
for
(entry = data->
timermap
; entry; prev = entry, entry = entry->
next
) {
352
if
(entry->
timerID
==
id
) {
353
if
(prev) {
354
prev->
next
= entry->
next
;
355
}
else
{
356
data->
timermap
= entry->
next
;
357
}
358
break
;
359
}
360
}
361
SDL_UnlockMutex
(data->
timermap_lock
);
362
363
if
(entry) {
364
if
(!
SDL_AtomicGet
(&entry->
timer
->
canceled
)) {
365
SDL_AtomicSet
(&entry->
timer
->
canceled
, 1);
366
canceled =
SDL_TRUE
;
367
}
368
SDL_free
(entry);
369
}
370
return
canceled;
371
}
372
373
/* vi: set ts=4 sw=4 expandtab: */
src
timer
SDL_timer.c
Generated on Sun Jun 26 2022 23:07:14 for SDL by
1.8.1.2