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