slouken@5535
|
1 |
/*
|
slouken@7517
|
2 |
Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
|
slouken@5535
|
3 |
|
slouken@5535
|
4 |
This software is provided 'as-is', without any express or implied
|
slouken@5535
|
5 |
warranty. In no event will the authors be held liable for any damages
|
slouken@5535
|
6 |
arising from the use of this software.
|
slouken@5535
|
7 |
|
slouken@5535
|
8 |
Permission is granted to anyone to use this software for any purpose,
|
slouken@5535
|
9 |
including commercial applications, and to alter it and redistribute it
|
slouken@5535
|
10 |
freely.
|
slouken@5535
|
11 |
*/
|
slouken@3338
|
12 |
#include <stdio.h>
|
slouken@5006
|
13 |
|
bob@3180
|
14 |
#include "SDL.h"
|
slouken@5006
|
15 |
#include "SDL_atomic.h"
|
slouken@5004
|
16 |
#include "SDL_assert.h"
|
slouken@5116
|
17 |
#include "SDL_cpuinfo.h"
|
bob@3180
|
18 |
|
bob@3201
|
19 |
/*
|
bob@3202
|
20 |
Absolutely basic tests just to see if we get the expected value
|
bob@3202
|
21 |
after calling each function.
|
bob@3201
|
22 |
*/
|
bob@3201
|
23 |
|
slouken@5004
|
24 |
static
|
slouken@3338
|
25 |
char *
|
bob@3202
|
26 |
tf(SDL_bool tf)
|
bob@3202
|
27 |
{
|
slouken@5003
|
28 |
static char *t = "TRUE";
|
slouken@5003
|
29 |
static char *f = "FALSE";
|
bob@3202
|
30 |
|
slouken@5003
|
31 |
if (tf)
|
slouken@5003
|
32 |
{
|
slouken@5003
|
33 |
return t;
|
slouken@5003
|
34 |
}
|
bob@3202
|
35 |
|
slouken@5003
|
36 |
return f;
|
bob@3202
|
37 |
}
|
slouken@3338
|
38 |
|
slouken@5004
|
39 |
static
|
slouken@5004
|
40 |
void RunBasicTest()
|
bob@3180
|
41 |
{
|
slouken@5003
|
42 |
int value;
|
slouken@5003
|
43 |
SDL_SpinLock lock = 0;
|
bob@3261
|
44 |
|
slouken@5013
|
45 |
SDL_atomic_t v;
|
slouken@5003
|
46 |
SDL_bool tfret = SDL_FALSE;
|
bob@3202
|
47 |
|
aschiffler@7639
|
48 |
SDL_Log("\nspin lock---------------------------------------\n\n");
|
bob@3202
|
49 |
|
slouken@5003
|
50 |
SDL_AtomicLock(&lock);
|
aschiffler@7639
|
51 |
SDL_Log("AtomicLock lock=%d\n", lock);
|
slouken@5003
|
52 |
SDL_AtomicUnlock(&lock);
|
aschiffler@7639
|
53 |
SDL_Log("AtomicUnlock lock=%d\n", lock);
|
bob@3202
|
54 |
|
aschiffler@7639
|
55 |
SDL_Log("\natomic -----------------------------------------\n\n");
|
slouken@7191
|
56 |
|
slouken@5003
|
57 |
SDL_AtomicSet(&v, 0);
|
aschiffler@7639
|
58 |
tfret = SDL_AtomicSet(&v, 10) == 0 ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
59 |
SDL_Log("AtomicSet(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
aschiffler@7639
|
60 |
tfret = SDL_AtomicAdd(&v, 10) == 10 ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
61 |
SDL_Log("AtomicAdd(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
bob@3202
|
62 |
|
slouken@5003
|
63 |
SDL_AtomicSet(&v, 0);
|
slouken@5003
|
64 |
SDL_AtomicIncRef(&v);
|
aschiffler@7639
|
65 |
tfret = (SDL_AtomicGet(&v) == 1) ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
66 |
SDL_Log("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
slouken@5003
|
67 |
SDL_AtomicIncRef(&v);
|
aschiffler@7639
|
68 |
tfret = (SDL_AtomicGet(&v) == 2) ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
69 |
SDL_Log("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
aschiffler@7639
|
70 |
tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
71 |
SDL_Log("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
aschiffler@7639
|
72 |
tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
73 |
SDL_Log("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
bob@3202
|
74 |
|
slouken@5003
|
75 |
SDL_AtomicSet(&v, 10);
|
aschiffler@7639
|
76 |
tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
77 |
SDL_Log("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
slouken@5003
|
78 |
value = SDL_AtomicGet(&v);
|
aschiffler@7639
|
79 |
tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
|
aschiffler@7639
|
80 |
SDL_Log("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
|
slouken@5004
|
81 |
}
|
bob@3202
|
82 |
|
slouken@5018
|
83 |
/**************************************************************************/
|
slouken@5018
|
84 |
/* Atomic operation test
|
slouken@5018
|
85 |
* Adapted with permission from code by Michael Davidsaver at:
|
slouken@5018
|
86 |
* http://bazaar.launchpad.net/~mdavidsaver/epics-base/atomic/revision/12105#src/libCom/test/epicsAtomicTest.c
|
slouken@5018
|
87 |
* Original copyright 2010 Brookhaven Science Associates as operator of Brookhaven National Lab
|
slouken@5018
|
88 |
* http://www.aps.anl.gov/epics/license/open.php
|
slouken@5018
|
89 |
*/
|
slouken@5004
|
90 |
|
slouken@5004
|
91 |
/* Tests semantics of atomic operations. Also a stress test
|
slouken@5004
|
92 |
* to see if they are really atomic.
|
slouken@5004
|
93 |
*
|
philipp@7538
|
94 |
* Several threads adding to the same variable.
|
slouken@5004
|
95 |
* at the end the value is compared with the expected
|
slouken@5004
|
96 |
* and with a non-atomic counter.
|
slouken@5004
|
97 |
*/
|
slouken@7191
|
98 |
|
slouken@5004
|
99 |
/* Number of concurrent incrementers */
|
slouken@5004
|
100 |
#define NThreads 2
|
slouken@5004
|
101 |
#define CountInc 100
|
slouken@5004
|
102 |
#define VALBITS (sizeof(atomicValue)*8)
|
slouken@7191
|
103 |
|
slouken@5004
|
104 |
#define atomicValue int
|
slouken@5004
|
105 |
#define CountTo ((atomicValue)((unsigned int)(1<<(VALBITS-1))-1))
|
slouken@5004
|
106 |
#define NInter (CountTo/CountInc/NThreads)
|
slouken@5004
|
107 |
#define Expect (CountTo-NInter*CountInc*NThreads)
|
slouken@7191
|
108 |
|
slouken@5004
|
109 |
SDL_COMPILE_TIME_ASSERT(size, CountTo>0); /* check for rollover */
|
slouken@7191
|
110 |
|
slouken@5004
|
111 |
static SDL_atomic_t good = { 42 };
|
slouken@7191
|
112 |
|
slouken@5004
|
113 |
static atomicValue bad = 42;
|
slouken@7191
|
114 |
|
slouken@5004
|
115 |
static SDL_atomic_t threadsRunning;
|
slouken@5004
|
116 |
|
slouken@5004
|
117 |
static SDL_sem *threadDone;
|
slouken@7191
|
118 |
|
slouken@5004
|
119 |
static
|
slouken@5004
|
120 |
int adder(void* junk)
|
slouken@5004
|
121 |
{
|
slouken@5004
|
122 |
unsigned long N=NInter;
|
aschiffler@7639
|
123 |
SDL_Log("Thread subtracting %d %lu times\n",CountInc,N);
|
slouken@5004
|
124 |
while (N--) {
|
slouken@5004
|
125 |
SDL_AtomicAdd(&good, -CountInc);
|
slouken@5004
|
126 |
bad-=CountInc;
|
slouken@5004
|
127 |
}
|
slouken@5004
|
128 |
SDL_AtomicAdd(&threadsRunning, -1);
|
slouken@5004
|
129 |
SDL_SemPost(threadDone);
|
slouken@5003
|
130 |
return 0;
|
slouken@5003
|
131 |
}
|
slouken@7191
|
132 |
|
slouken@5004
|
133 |
static
|
slouken@5004
|
134 |
void runAdder(void)
|
slouken@5004
|
135 |
{
|
slouken@5004
|
136 |
Uint32 start, end;
|
slouken@5004
|
137 |
int T=NThreads;
|
slouken@7191
|
138 |
|
slouken@5004
|
139 |
start = SDL_GetTicks();
|
slouken@7191
|
140 |
|
slouken@5004
|
141 |
threadDone = SDL_CreateSemaphore(0);
|
slouken@5004
|
142 |
|
slouken@5004
|
143 |
SDL_AtomicSet(&threadsRunning, NThreads);
|
slouken@5004
|
144 |
|
slouken@5004
|
145 |
while (T--)
|
icculus@5969
|
146 |
SDL_CreateThread(adder, "Adder", NULL);
|
slouken@7191
|
147 |
|
slouken@5004
|
148 |
while (SDL_AtomicGet(&threadsRunning) > 0)
|
slouken@5004
|
149 |
SDL_SemWait(threadDone);
|
slouken@7191
|
150 |
|
slouken@5004
|
151 |
SDL_DestroySemaphore(threadDone);
|
slouken@5004
|
152 |
|
slouken@5004
|
153 |
end = SDL_GetTicks();
|
slouken@7191
|
154 |
|
aschiffler@7639
|
155 |
SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
|
slouken@5004
|
156 |
}
|
slouken@7191
|
157 |
|
slouken@5004
|
158 |
static
|
slouken@5004
|
159 |
void RunEpicTest()
|
slouken@5004
|
160 |
{
|
slouken@5004
|
161 |
int b;
|
slouken@5004
|
162 |
atomicValue v;
|
slouken@7191
|
163 |
|
aschiffler@7639
|
164 |
SDL_Log("\nepic test---------------------------------------\n\n");
|
slouken@5004
|
165 |
|
aschiffler@7639
|
166 |
SDL_Log("Size asserted to be >= 32-bit\n");
|
slouken@5004
|
167 |
SDL_assert(sizeof(atomicValue)>=4);
|
slouken@7191
|
168 |
|
aschiffler@7639
|
169 |
SDL_Log("Check static initializer\n");
|
slouken@5004
|
170 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
171 |
SDL_assert(v==42);
|
slouken@7191
|
172 |
|
slouken@5004
|
173 |
SDL_assert(bad==42);
|
slouken@7191
|
174 |
|
aschiffler@7639
|
175 |
SDL_Log("Test negative values\n");
|
slouken@5004
|
176 |
SDL_AtomicSet(&good, -5);
|
slouken@5004
|
177 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
178 |
SDL_assert(v==-5);
|
slouken@7191
|
179 |
|
aschiffler@7639
|
180 |
SDL_Log("Verify maximum value\n");
|
slouken@5004
|
181 |
SDL_AtomicSet(&good, CountTo);
|
slouken@5004
|
182 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
183 |
SDL_assert(v==CountTo);
|
slouken@7191
|
184 |
|
aschiffler@7639
|
185 |
SDL_Log("Test compare and exchange\n");
|
slouken@7191
|
186 |
|
slouken@5004
|
187 |
b=SDL_AtomicCAS(&good, 500, 43);
|
slouken@5004
|
188 |
SDL_assert(!b); /* no swap since CountTo!=500 */
|
slouken@5004
|
189 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
190 |
SDL_assert(v==CountTo); /* ensure no swap */
|
slouken@7191
|
191 |
|
slouken@5004
|
192 |
b=SDL_AtomicCAS(&good, CountTo, 44);
|
slouken@5004
|
193 |
SDL_assert(!!b); /* will swap */
|
slouken@5004
|
194 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
195 |
SDL_assert(v==44);
|
slouken@7191
|
196 |
|
aschiffler@7639
|
197 |
SDL_Log("Test Add\n");
|
slouken@7191
|
198 |
|
slouken@5004
|
199 |
v=SDL_AtomicAdd(&good, 1);
|
slouken@5004
|
200 |
SDL_assert(v==44);
|
slouken@5004
|
201 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
202 |
SDL_assert(v==45);
|
slouken@7191
|
203 |
|
slouken@5004
|
204 |
v=SDL_AtomicAdd(&good, 10);
|
slouken@5004
|
205 |
SDL_assert(v==45);
|
slouken@5004
|
206 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
207 |
SDL_assert(v==55);
|
slouken@7191
|
208 |
|
aschiffler@7639
|
209 |
SDL_Log("Test Add (Negative values)\n");
|
slouken@7191
|
210 |
|
slouken@5004
|
211 |
v=SDL_AtomicAdd(&good, -20);
|
slouken@5004
|
212 |
SDL_assert(v==55);
|
slouken@5004
|
213 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
214 |
SDL_assert(v==35);
|
slouken@7191
|
215 |
|
slouken@5004
|
216 |
v=SDL_AtomicAdd(&good, -50); /* crossing zero down */
|
slouken@5004
|
217 |
SDL_assert(v==35);
|
slouken@5004
|
218 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
219 |
SDL_assert(v==-15);
|
slouken@7191
|
220 |
|
slouken@5004
|
221 |
v=SDL_AtomicAdd(&good, 30); /* crossing zero up */
|
slouken@5004
|
222 |
SDL_assert(v==-15);
|
slouken@5004
|
223 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
224 |
SDL_assert(v==15);
|
slouken@7191
|
225 |
|
aschiffler@7639
|
226 |
SDL_Log("Reset before count down test\n");
|
slouken@5004
|
227 |
SDL_AtomicSet(&good, CountTo);
|
slouken@5004
|
228 |
v=SDL_AtomicGet(&good);
|
slouken@5004
|
229 |
SDL_assert(v==CountTo);
|
slouken@7191
|
230 |
|
slouken@5004
|
231 |
bad=CountTo;
|
slouken@5004
|
232 |
SDL_assert(bad==CountTo);
|
slouken@7191
|
233 |
|
aschiffler@7639
|
234 |
SDL_Log("Counting down from %d, Expect %d remaining\n",CountTo,Expect);
|
slouken@5004
|
235 |
runAdder();
|
slouken@7191
|
236 |
|
slouken@5004
|
237 |
v=SDL_AtomicGet(&good);
|
aschiffler@7639
|
238 |
SDL_Log("Atomic %d Non-Atomic %d\n",v,bad);
|
slouken@5004
|
239 |
SDL_assert(v==Expect);
|
slouken@5004
|
240 |
SDL_assert(bad!=Expect);
|
slouken@5004
|
241 |
}
|
slouken@5004
|
242 |
|
slouken@5018
|
243 |
/* End atomic operation test */
|
slouken@5018
|
244 |
/**************************************************************************/
|
slouken@5018
|
245 |
|
slouken@5098
|
246 |
/**************************************************************************/
|
slouken@5098
|
247 |
/* Lock-free FIFO test */
|
slouken@5098
|
248 |
|
slouken@5099
|
249 |
/* This is useful to test the impact of another thread locking the queue
|
slouken@5099
|
250 |
entirely for heavy-weight manipulation.
|
slouken@5099
|
251 |
*/
|
slouken@5099
|
252 |
#define TEST_SPINLOCK_FIFO
|
slouken@5099
|
253 |
|
slouken@5098
|
254 |
#define NUM_READERS 4
|
slouken@5098
|
255 |
#define NUM_WRITERS 4
|
slouken@5098
|
256 |
#define EVENTS_PER_WRITER 1000000
|
slouken@5098
|
257 |
|
slouken@5098
|
258 |
/* The number of entries must be a power of 2 */
|
slouken@5098
|
259 |
#define MAX_ENTRIES 256
|
slouken@5098
|
260 |
#define WRAP_MASK (MAX_ENTRIES-1)
|
slouken@5098
|
261 |
|
slouken@5098
|
262 |
typedef struct
|
slouken@5098
|
263 |
{
|
slouken@5098
|
264 |
SDL_atomic_t sequence;
|
slouken@5098
|
265 |
SDL_Event event;
|
slouken@5098
|
266 |
} SDL_EventQueueEntry;
|
slouken@5098
|
267 |
|
slouken@5098
|
268 |
typedef struct
|
slouken@5098
|
269 |
{
|
slouken@5098
|
270 |
SDL_EventQueueEntry entries[MAX_ENTRIES];
|
slouken@5098
|
271 |
|
slouken@5115
|
272 |
char cache_pad1[SDL_CACHELINE_SIZE-((sizeof(SDL_EventQueueEntry)*MAX_ENTRIES)%SDL_CACHELINE_SIZE)];
|
slouken@5098
|
273 |
|
slouken@5098
|
274 |
SDL_atomic_t enqueue_pos;
|
slouken@5098
|
275 |
|
slouken@5115
|
276 |
char cache_pad2[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
|
slouken@5098
|
277 |
|
slouken@5098
|
278 |
SDL_atomic_t dequeue_pos;
|
slouken@5098
|
279 |
|
slouken@5115
|
280 |
char cache_pad3[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
|
slouken@5098
|
281 |
|
slouken@5099
|
282 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
283 |
SDL_SpinLock lock;
|
slouken@5099
|
284 |
SDL_atomic_t rwcount;
|
slouken@5099
|
285 |
SDL_atomic_t watcher;
|
slouken@5099
|
286 |
|
slouken@5115
|
287 |
char cache_pad4[SDL_CACHELINE_SIZE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)];
|
slouken@5099
|
288 |
#endif
|
slouken@5099
|
289 |
|
slouken@5102
|
290 |
volatile SDL_bool active;
|
slouken@5098
|
291 |
|
slouken@5098
|
292 |
/* Only needed for the mutex test */
|
slouken@5098
|
293 |
SDL_mutex *mutex;
|
slouken@5098
|
294 |
|
slouken@5098
|
295 |
} SDL_EventQueue;
|
slouken@5098
|
296 |
|
slouken@5098
|
297 |
static void InitEventQueue(SDL_EventQueue *queue)
|
slouken@5098
|
298 |
{
|
slouken@5098
|
299 |
int i;
|
slouken@5098
|
300 |
|
slouken@5098
|
301 |
for (i = 0; i < MAX_ENTRIES; ++i) {
|
slouken@5098
|
302 |
SDL_AtomicSet(&queue->entries[i].sequence, i);
|
slouken@5098
|
303 |
}
|
slouken@5098
|
304 |
SDL_AtomicSet(&queue->enqueue_pos, 0);
|
slouken@5098
|
305 |
SDL_AtomicSet(&queue->dequeue_pos, 0);
|
slouken@5099
|
306 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
307 |
queue->lock = 0;
|
slouken@5099
|
308 |
SDL_AtomicSet(&queue->rwcount, 0);
|
slouken@5099
|
309 |
#endif
|
slouken@5098
|
310 |
queue->active = SDL_TRUE;
|
slouken@5098
|
311 |
}
|
slouken@5098
|
312 |
|
slouken@5098
|
313 |
static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
|
slouken@5098
|
314 |
{
|
slouken@5098
|
315 |
SDL_EventQueueEntry *entry;
|
slouken@5098
|
316 |
unsigned queue_pos;
|
slouken@5098
|
317 |
unsigned entry_seq;
|
slouken@5098
|
318 |
int delta;
|
slouken@5099
|
319 |
SDL_bool status;
|
slouken@5099
|
320 |
|
slouken@5099
|
321 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
322 |
/* This is a gate so an external thread can lock the queue */
|
slouken@5099
|
323 |
SDL_AtomicLock(&queue->lock);
|
slouken@5099
|
324 |
SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
|
slouken@5099
|
325 |
SDL_AtomicIncRef(&queue->rwcount);
|
slouken@5099
|
326 |
SDL_AtomicUnlock(&queue->lock);
|
slouken@5099
|
327 |
#endif
|
slouken@5098
|
328 |
|
slouken@5098
|
329 |
queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
|
slouken@5098
|
330 |
for ( ; ; ) {
|
slouken@5098
|
331 |
entry = &queue->entries[queue_pos & WRAP_MASK];
|
slouken@5098
|
332 |
entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
|
slouken@5098
|
333 |
|
slouken@5098
|
334 |
delta = (int)(entry_seq - queue_pos);
|
slouken@5098
|
335 |
if (delta == 0) {
|
slouken@5098
|
336 |
/* The entry and the queue position match, try to increment the queue position */
|
slouken@5098
|
337 |
if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
|
slouken@5099
|
338 |
/* We own the object, fill it! */
|
slouken@5099
|
339 |
entry->event = *event;
|
slouken@5099
|
340 |
SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
|
slouken@5099
|
341 |
status = SDL_TRUE;
|
slouken@5098
|
342 |
break;
|
slouken@5098
|
343 |
}
|
slouken@5098
|
344 |
} else if (delta < 0) {
|
slouken@5098
|
345 |
/* We ran into an old queue entry, which means it still needs to be dequeued */
|
slouken@5099
|
346 |
status = SDL_FALSE;
|
slouken@5099
|
347 |
break;
|
slouken@5098
|
348 |
} else {
|
slouken@5098
|
349 |
/* We ran into a new queue entry, get the new queue position */
|
slouken@5098
|
350 |
queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
|
slouken@5098
|
351 |
}
|
slouken@5098
|
352 |
}
|
slouken@5098
|
353 |
|
slouken@5099
|
354 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
355 |
SDL_AtomicDecRef(&queue->rwcount);
|
slouken@5099
|
356 |
#endif
|
slouken@5099
|
357 |
return status;
|
slouken@5098
|
358 |
}
|
slouken@5098
|
359 |
|
slouken@5098
|
360 |
static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
|
slouken@5098
|
361 |
{
|
slouken@5098
|
362 |
SDL_EventQueueEntry *entry;
|
slouken@5098
|
363 |
unsigned queue_pos;
|
slouken@5098
|
364 |
unsigned entry_seq;
|
slouken@5098
|
365 |
int delta;
|
slouken@5099
|
366 |
SDL_bool status;
|
slouken@5099
|
367 |
|
slouken@5099
|
368 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
369 |
/* This is a gate so an external thread can lock the queue */
|
slouken@5099
|
370 |
SDL_AtomicLock(&queue->lock);
|
slouken@5099
|
371 |
SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
|
slouken@5099
|
372 |
SDL_AtomicIncRef(&queue->rwcount);
|
slouken@5099
|
373 |
SDL_AtomicUnlock(&queue->lock);
|
slouken@5099
|
374 |
#endif
|
slouken@5098
|
375 |
|
slouken@5098
|
376 |
queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
|
slouken@5098
|
377 |
for ( ; ; ) {
|
slouken@5098
|
378 |
entry = &queue->entries[queue_pos & WRAP_MASK];
|
slouken@5098
|
379 |
entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
|
slouken@5098
|
380 |
|
slouken@5098
|
381 |
delta = (int)(entry_seq - (queue_pos + 1));
|
slouken@5098
|
382 |
if (delta == 0) {
|
slouken@5098
|
383 |
/* The entry and the queue position match, try to increment the queue position */
|
slouken@5098
|
384 |
if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
|
slouken@5099
|
385 |
/* We own the object, fill it! */
|
slouken@5099
|
386 |
*event = entry->event;
|
slouken@5099
|
387 |
SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
|
slouken@5099
|
388 |
status = SDL_TRUE;
|
slouken@5098
|
389 |
break;
|
slouken@5098
|
390 |
}
|
slouken@5098
|
391 |
} else if (delta < 0) {
|
slouken@5098
|
392 |
/* We ran into an old queue entry, which means we've hit empty */
|
slouken@5099
|
393 |
status = SDL_FALSE;
|
slouken@5099
|
394 |
break;
|
slouken@5098
|
395 |
} else {
|
slouken@5098
|
396 |
/* We ran into a new queue entry, get the new queue position */
|
slouken@5098
|
397 |
queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
|
slouken@5098
|
398 |
}
|
slouken@5098
|
399 |
}
|
slouken@5098
|
400 |
|
slouken@5099
|
401 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
402 |
SDL_AtomicDecRef(&queue->rwcount);
|
slouken@5099
|
403 |
#endif
|
slouken@5099
|
404 |
return status;
|
slouken@5098
|
405 |
}
|
slouken@5098
|
406 |
|
slouken@5098
|
407 |
static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
|
slouken@5098
|
408 |
{
|
slouken@5098
|
409 |
SDL_EventQueueEntry *entry;
|
slouken@5098
|
410 |
unsigned queue_pos;
|
slouken@5098
|
411 |
unsigned entry_seq;
|
slouken@5098
|
412 |
int delta;
|
slouken@5099
|
413 |
SDL_bool status = SDL_FALSE;
|
slouken@5098
|
414 |
|
slouken@6977
|
415 |
SDL_LockMutex(queue->mutex);
|
slouken@5098
|
416 |
|
slouken@5098
|
417 |
queue_pos = (unsigned)queue->enqueue_pos.value;
|
slouken@5098
|
418 |
entry = &queue->entries[queue_pos & WRAP_MASK];
|
slouken@5098
|
419 |
entry_seq = (unsigned)entry->sequence.value;
|
slouken@5098
|
420 |
|
slouken@5098
|
421 |
delta = (int)(entry_seq - queue_pos);
|
slouken@5098
|
422 |
if (delta == 0) {
|
slouken@5098
|
423 |
++queue->enqueue_pos.value;
|
slouken@5099
|
424 |
|
slouken@5099
|
425 |
/* We own the object, fill it! */
|
slouken@5099
|
426 |
entry->event = *event;
|
slouken@5099
|
427 |
entry->sequence.value = (int)(queue_pos + 1);
|
slouken@5099
|
428 |
status = SDL_TRUE;
|
slouken@5098
|
429 |
} else if (delta < 0) {
|
slouken@5098
|
430 |
/* We ran into an old queue entry, which means it still needs to be dequeued */
|
slouken@5098
|
431 |
} else {
|
aschiffler@7639
|
432 |
SDL_Log("ERROR: mutex failed!\n");
|
slouken@5098
|
433 |
}
|
slouken@5098
|
434 |
|
slouken@6977
|
435 |
SDL_UnlockMutex(queue->mutex);
|
slouken@5098
|
436 |
|
slouken@5099
|
437 |
return status;
|
slouken@5098
|
438 |
}
|
slouken@5098
|
439 |
|
slouken@5098
|
440 |
static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
|
slouken@5098
|
441 |
{
|
slouken@5098
|
442 |
SDL_EventQueueEntry *entry;
|
slouken@5098
|
443 |
unsigned queue_pos;
|
slouken@5098
|
444 |
unsigned entry_seq;
|
slouken@5098
|
445 |
int delta;
|
slouken@5099
|
446 |
SDL_bool status = SDL_FALSE;
|
slouken@5098
|
447 |
|
slouken@6977
|
448 |
SDL_LockMutex(queue->mutex);
|
slouken@5098
|
449 |
|
slouken@5098
|
450 |
queue_pos = (unsigned)queue->dequeue_pos.value;
|
slouken@5098
|
451 |
entry = &queue->entries[queue_pos & WRAP_MASK];
|
slouken@5098
|
452 |
entry_seq = (unsigned)entry->sequence.value;
|
slouken@5098
|
453 |
|
slouken@5098
|
454 |
delta = (int)(entry_seq - (queue_pos + 1));
|
slouken@5098
|
455 |
if (delta == 0) {
|
slouken@5098
|
456 |
++queue->dequeue_pos.value;
|
slouken@5099
|
457 |
|
slouken@5099
|
458 |
/* We own the object, fill it! */
|
slouken@5099
|
459 |
*event = entry->event;
|
slouken@5099
|
460 |
entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
|
slouken@5099
|
461 |
status = SDL_TRUE;
|
slouken@5098
|
462 |
} else if (delta < 0) {
|
slouken@5098
|
463 |
/* We ran into an old queue entry, which means we've hit empty */
|
slouken@5098
|
464 |
} else {
|
aschiffler@7639
|
465 |
SDL_Log("ERROR: mutex failed!\n");
|
slouken@5098
|
466 |
}
|
slouken@5098
|
467 |
|
slouken@6977
|
468 |
SDL_UnlockMutex(queue->mutex);
|
slouken@5098
|
469 |
|
slouken@5099
|
470 |
return status;
|
slouken@5098
|
471 |
}
|
slouken@5098
|
472 |
|
slouken@5098
|
473 |
static SDL_sem *writersDone;
|
slouken@5098
|
474 |
static SDL_sem *readersDone;
|
slouken@5098
|
475 |
static SDL_atomic_t writersRunning;
|
slouken@5098
|
476 |
static SDL_atomic_t readersRunning;
|
slouken@5098
|
477 |
|
slouken@5098
|
478 |
typedef struct
|
slouken@5098
|
479 |
{
|
slouken@5098
|
480 |
SDL_EventQueue *queue;
|
slouken@5098
|
481 |
int index;
|
slouken@5115
|
482 |
char padding1[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int))%SDL_CACHELINE_SIZE];
|
slouken@5098
|
483 |
int waits;
|
slouken@5098
|
484 |
SDL_bool lock_free;
|
slouken@5115
|
485 |
char padding2[SDL_CACHELINE_SIZE-sizeof(int)-sizeof(SDL_bool)];
|
slouken@5098
|
486 |
} WriterData;
|
slouken@5098
|
487 |
|
slouken@5098
|
488 |
typedef struct
|
slouken@5098
|
489 |
{
|
slouken@5098
|
490 |
SDL_EventQueue *queue;
|
slouken@5098
|
491 |
int counters[NUM_WRITERS];
|
slouken@5098
|
492 |
int waits;
|
slouken@5098
|
493 |
SDL_bool lock_free;
|
slouken@5115
|
494 |
char padding[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int)*NUM_WRITERS+sizeof(int)+sizeof(SDL_bool))%SDL_CACHELINE_SIZE];
|
slouken@5098
|
495 |
} ReaderData;
|
slouken@5098
|
496 |
|
slouken@5098
|
497 |
static int FIFO_Writer(void* _data)
|
slouken@5098
|
498 |
{
|
slouken@5098
|
499 |
WriterData *data = (WriterData *)_data;
|
slouken@5098
|
500 |
SDL_EventQueue *queue = data->queue;
|
slouken@5098
|
501 |
int i;
|
slouken@5098
|
502 |
SDL_Event event;
|
slouken@5098
|
503 |
|
slouken@5098
|
504 |
event.type = SDL_USEREVENT;
|
slouken@5098
|
505 |
event.user.windowID = 0;
|
slouken@5098
|
506 |
event.user.code = 0;
|
slouken@5098
|
507 |
event.user.data1 = data;
|
slouken@5098
|
508 |
event.user.data2 = NULL;
|
slouken@5098
|
509 |
|
slouken@5098
|
510 |
if (data->lock_free) {
|
slouken@5098
|
511 |
for (i = 0; i < EVENTS_PER_WRITER; ++i) {
|
slouken@5098
|
512 |
event.user.code = i;
|
slouken@5098
|
513 |
while (!EnqueueEvent_LockFree(queue, &event)) {
|
slouken@5098
|
514 |
++data->waits;
|
slouken@5098
|
515 |
SDL_Delay(0);
|
slouken@5098
|
516 |
}
|
slouken@5098
|
517 |
}
|
slouken@5098
|
518 |
} else {
|
slouken@5098
|
519 |
for (i = 0; i < EVENTS_PER_WRITER; ++i) {
|
slouken@5098
|
520 |
event.user.code = i;
|
slouken@5098
|
521 |
while (!EnqueueEvent_Mutex(queue, &event)) {
|
slouken@5098
|
522 |
++data->waits;
|
slouken@5098
|
523 |
SDL_Delay(0);
|
slouken@5098
|
524 |
}
|
slouken@5098
|
525 |
}
|
slouken@5098
|
526 |
}
|
slouken@5098
|
527 |
SDL_AtomicAdd(&writersRunning, -1);
|
slouken@5098
|
528 |
SDL_SemPost(writersDone);
|
slouken@5098
|
529 |
return 0;
|
slouken@5098
|
530 |
}
|
slouken@5098
|
531 |
|
slouken@5098
|
532 |
static int FIFO_Reader(void* _data)
|
slouken@5098
|
533 |
{
|
slouken@5098
|
534 |
ReaderData *data = (ReaderData *)_data;
|
slouken@5098
|
535 |
SDL_EventQueue *queue = data->queue;
|
slouken@5098
|
536 |
SDL_Event event;
|
slouken@5098
|
537 |
|
slouken@5098
|
538 |
if (data->lock_free) {
|
slouken@5098
|
539 |
for ( ; ; ) {
|
slouken@5098
|
540 |
if (DequeueEvent_LockFree(queue, &event)) {
|
slouken@5098
|
541 |
WriterData *writer = (WriterData*)event.user.data1;
|
slouken@5098
|
542 |
++data->counters[writer->index];
|
slouken@5098
|
543 |
} else if (queue->active) {
|
slouken@5098
|
544 |
++data->waits;
|
slouken@5098
|
545 |
SDL_Delay(0);
|
slouken@5098
|
546 |
} else {
|
slouken@5098
|
547 |
/* We drained the queue, we're done! */
|
slouken@5098
|
548 |
break;
|
slouken@5098
|
549 |
}
|
slouken@5098
|
550 |
}
|
slouken@5098
|
551 |
} else {
|
slouken@5098
|
552 |
for ( ; ; ) {
|
slouken@5098
|
553 |
if (DequeueEvent_Mutex(queue, &event)) {
|
slouken@5098
|
554 |
WriterData *writer = (WriterData*)event.user.data1;
|
slouken@5098
|
555 |
++data->counters[writer->index];
|
slouken@5098
|
556 |
} else if (queue->active) {
|
slouken@5098
|
557 |
++data->waits;
|
slouken@5098
|
558 |
SDL_Delay(0);
|
slouken@5098
|
559 |
} else {
|
slouken@5098
|
560 |
/* We drained the queue, we're done! */
|
slouken@5098
|
561 |
break;
|
slouken@5098
|
562 |
}
|
slouken@5098
|
563 |
}
|
slouken@5098
|
564 |
}
|
slouken@5098
|
565 |
SDL_AtomicAdd(&readersRunning, -1);
|
slouken@5098
|
566 |
SDL_SemPost(readersDone);
|
slouken@5098
|
567 |
return 0;
|
slouken@5098
|
568 |
}
|
slouken@5098
|
569 |
|
slouken@5099
|
570 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
571 |
/* This thread periodically locks the queue for no particular reason */
|
slouken@5099
|
572 |
static int FIFO_Watcher(void* _data)
|
slouken@5099
|
573 |
{
|
slouken@5099
|
574 |
SDL_EventQueue *queue = (SDL_EventQueue *)_data;
|
slouken@5099
|
575 |
|
slouken@5099
|
576 |
while (queue->active) {
|
slouken@5099
|
577 |
SDL_AtomicLock(&queue->lock);
|
slouken@5099
|
578 |
SDL_AtomicIncRef(&queue->watcher);
|
slouken@5099
|
579 |
while (SDL_AtomicGet(&queue->rwcount) > 0) {
|
slouken@5099
|
580 |
SDL_Delay(0);
|
slouken@5099
|
581 |
}
|
slouken@5099
|
582 |
/* Do queue manipulation here... */
|
slouken@5099
|
583 |
SDL_AtomicDecRef(&queue->watcher);
|
slouken@5099
|
584 |
SDL_AtomicUnlock(&queue->lock);
|
slouken@5099
|
585 |
|
slouken@5099
|
586 |
/* Wait a bit... */
|
slouken@5099
|
587 |
SDL_Delay(1);
|
slouken@5099
|
588 |
}
|
slouken@5099
|
589 |
return 0;
|
slouken@5099
|
590 |
}
|
slouken@5099
|
591 |
#endif /* TEST_SPINLOCK_FIFO */
|
slouken@5099
|
592 |
|
slouken@5098
|
593 |
static void RunFIFOTest(SDL_bool lock_free)
|
slouken@5098
|
594 |
{
|
slouken@5098
|
595 |
SDL_EventQueue queue;
|
slouken@5098
|
596 |
WriterData writerData[NUM_WRITERS];
|
slouken@5098
|
597 |
ReaderData readerData[NUM_READERS];
|
slouken@5098
|
598 |
Uint32 start, end;
|
slouken@5098
|
599 |
int i, j;
|
slouken@5098
|
600 |
int grand_total;
|
aschiffler@7639
|
601 |
char textBuffer[1024];
|
aschiffler@7639
|
602 |
int len;
|
slouken@7191
|
603 |
|
aschiffler@7639
|
604 |
SDL_Log("\nFIFO test---------------------------------------\n\n");
|
aschiffler@7639
|
605 |
SDL_Log("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
|
slouken@5098
|
606 |
|
slouken@5098
|
607 |
readersDone = SDL_CreateSemaphore(0);
|
slouken@5098
|
608 |
writersDone = SDL_CreateSemaphore(0);
|
slouken@5098
|
609 |
|
slouken@5098
|
610 |
SDL_memset(&queue, 0xff, sizeof(queue));
|
slouken@5098
|
611 |
|
slouken@5098
|
612 |
InitEventQueue(&queue);
|
slouken@5098
|
613 |
if (!lock_free) {
|
slouken@5098
|
614 |
queue.mutex = SDL_CreateMutex();
|
slouken@5098
|
615 |
}
|
slouken@5098
|
616 |
|
slouken@5098
|
617 |
start = SDL_GetTicks();
|
slouken@7191
|
618 |
|
slouken@5099
|
619 |
#ifdef TEST_SPINLOCK_FIFO
|
slouken@5099
|
620 |
/* Start a monitoring thread */
|
slouken@5099
|
621 |
if (lock_free) {
|
icculus@5969
|
622 |
SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
|
slouken@5099
|
623 |
}
|
slouken@5099
|
624 |
#endif
|
slouken@5099
|
625 |
|
slouken@5098
|
626 |
/* Start the readers first */
|
aschiffler@7639
|
627 |
SDL_Log("Starting %d readers\n", NUM_READERS);
|
slouken@5098
|
628 |
SDL_zero(readerData);
|
slouken@5098
|
629 |
SDL_AtomicSet(&readersRunning, NUM_READERS);
|
slouken@5098
|
630 |
for (i = 0; i < NUM_READERS; ++i) {
|
icculus@5969
|
631 |
char name[64];
|
icculus@5969
|
632 |
SDL_snprintf(name, sizeof (name), "FIFOReader%d", i);
|
slouken@5098
|
633 |
readerData[i].queue = &queue;
|
slouken@5098
|
634 |
readerData[i].lock_free = lock_free;
|
icculus@5969
|
635 |
SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
|
slouken@5098
|
636 |
}
|
slouken@5098
|
637 |
|
slouken@5098
|
638 |
/* Start up the writers */
|
aschiffler@7639
|
639 |
SDL_Log("Starting %d writers\n", NUM_WRITERS);
|
slouken@5098
|
640 |
SDL_zero(writerData);
|
slouken@5098
|
641 |
SDL_AtomicSet(&writersRunning, NUM_WRITERS);
|
slouken@5098
|
642 |
for (i = 0; i < NUM_WRITERS; ++i) {
|
icculus@5969
|
643 |
char name[64];
|
icculus@5969
|
644 |
SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i);
|
slouken@5098
|
645 |
writerData[i].queue = &queue;
|
slouken@5098
|
646 |
writerData[i].index = i;
|
slouken@5098
|
647 |
writerData[i].lock_free = lock_free;
|
icculus@5969
|
648 |
SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
|
slouken@5098
|
649 |
}
|
slouken@7191
|
650 |
|
slouken@5098
|
651 |
/* Wait for the writers */
|
slouken@5098
|
652 |
while (SDL_AtomicGet(&writersRunning) > 0) {
|
slouken@5098
|
653 |
SDL_SemWait(writersDone);
|
slouken@5098
|
654 |
}
|
slouken@7191
|
655 |
|
slouken@5098
|
656 |
/* Shut down the queue so readers exit */
|
slouken@5098
|
657 |
queue.active = SDL_FALSE;
|
slouken@5098
|
658 |
|
slouken@5098
|
659 |
/* Wait for the readers */
|
slouken@5098
|
660 |
while (SDL_AtomicGet(&readersRunning) > 0) {
|
slouken@5098
|
661 |
SDL_SemWait(readersDone);
|
slouken@5098
|
662 |
}
|
slouken@5098
|
663 |
|
slouken@5098
|
664 |
end = SDL_GetTicks();
|
slouken@7191
|
665 |
|
slouken@5098
|
666 |
SDL_DestroySemaphore(readersDone);
|
slouken@5098
|
667 |
SDL_DestroySemaphore(writersDone);
|
slouken@5098
|
668 |
|
slouken@5098
|
669 |
if (!lock_free) {
|
slouken@5098
|
670 |
SDL_DestroyMutex(queue.mutex);
|
slouken@5098
|
671 |
}
|
slouken@7191
|
672 |
|
aschiffler@7639
|
673 |
SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
|
slouken@5098
|
674 |
|
aschiffler@7639
|
675 |
SDL_Log("\n");
|
slouken@5098
|
676 |
for (i = 0; i < NUM_WRITERS; ++i) {
|
aschiffler@7639
|
677 |
SDL_Log("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
|
slouken@5098
|
678 |
}
|
aschiffler@7639
|
679 |
SDL_Log("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
|
slouken@5098
|
680 |
|
slouken@5098
|
681 |
/* Print a breakdown of which readers read messages from which writer */
|
aschiffler@7639
|
682 |
SDL_Log("\n");
|
slouken@5098
|
683 |
grand_total = 0;
|
slouken@5098
|
684 |
for (i = 0; i < NUM_READERS; ++i) {
|
slouken@5098
|
685 |
int total = 0;
|
slouken@5098
|
686 |
for (j = 0; j < NUM_WRITERS; ++j) {
|
slouken@5098
|
687 |
total += readerData[i].counters[j];
|
slouken@5098
|
688 |
}
|
slouken@5098
|
689 |
grand_total += total;
|
aschiffler@7639
|
690 |
SDL_Log("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
|
aschiffler@7639
|
691 |
SDL_snprintf(textBuffer, sizeof(textBuffer), " { ");
|
slouken@5098
|
692 |
for (j = 0; j < NUM_WRITERS; ++j) {
|
slouken@5098
|
693 |
if (j > 0) {
|
aschiffler@7639
|
694 |
len = SDL_strlen(textBuffer);
|
aschiffler@7639
|
695 |
SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
|
slouken@5098
|
696 |
}
|
aschiffler@7639
|
697 |
len = SDL_strlen(textBuffer);
|
aschiffler@7639
|
698 |
SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]);
|
slouken@5098
|
699 |
}
|
aschiffler@7639
|
700 |
len = SDL_strlen(textBuffer);
|
aschiffler@7639
|
701 |
SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n");
|
aschiffler@7639
|
702 |
SDL_Log(textBuffer);
|
slouken@5098
|
703 |
}
|
aschiffler@7639
|
704 |
SDL_Log("Readers read %d total events\n", grand_total);
|
slouken@5098
|
705 |
}
|
slouken@5098
|
706 |
|
slouken@5098
|
707 |
/* End FIFO test */
|
slouken@5098
|
708 |
/**************************************************************************/
|
slouken@5098
|
709 |
|
slouken@5004
|
710 |
int
|
slouken@5004
|
711 |
main(int argc, char *argv[])
|
slouken@5004
|
712 |
{
|
aschiffler@7639
|
713 |
/* Enable standard application logging */
|
aschiffler@7639
|
714 |
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
aschiffler@7639
|
715 |
|
slouken@5004
|
716 |
RunBasicTest();
|
slouken@5004
|
717 |
RunEpicTest();
|
slouken@5098
|
718 |
/* This test is really slow, so don't run it by default */
|
slouken@5098
|
719 |
#if 0
|
slouken@5098
|
720 |
RunFIFOTest(SDL_FALSE);
|
slouken@5098
|
721 |
#endif
|
slouken@5098
|
722 |
RunFIFOTest(SDL_TRUE);
|
slouken@5004
|
723 |
return 0;
|
slouken@5004
|
724 |
}
|
slouken@5098
|
725 |
|
slouken@5098
|
726 |
/* vi: set ts=4 sw=4 expandtab: */
|