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