1/*
2 Copyright (C) 1997-2021 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
21static
22char *
23tf(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
36static
37void 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
106enum {
107 CountTo_GreaterThanZero = CountTo > 0,
108};
109SDL_COMPILE_TIME_ASSERT(size, CountTo_GreaterThanZero); /* check for rollover */
110
111static SDL_atomic_t good = { 42 };
112
113static atomicValue bad = 42;
114
115static SDL_atomic_t threadsRunning;
116
117static SDL_sem *threadDone;
118
119static
120int SDLCALL adder(void* junk)
121{
122 unsigned long N=NInter;
123 SDL_Log("Thread subtracting %d %lu times\n",CountInc,N);
124 while (N--) {
125 SDL_AtomicAdd(&good, -CountInc);
126 bad-=CountInc;
127 }
128 SDL_AtomicAdd(&threadsRunning, -1);
129 SDL_SemPost(threadDone);
130 return 0;
131}
132
133static
134void runAdder(void)
135{
136 Uint32 start, end;
137 int T=NThreads;
138
139 start = SDL_GetTicks();
140
141 threadDone = SDL_CreateSemaphore(0);
142
143 SDL_AtomicSet(&threadsRunning, NThreads);
144
145 while (T--)
146 SDL_CreateThread(adder, "Adder", NULL);
147
148 while (SDL_AtomicGet(&threadsRunning) > 0)
149 SDL_SemWait(threadDone);
150
151 SDL_DestroySemaphore(threadDone);
152
153 end = SDL_GetTicks();
154
155 SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
156}
157
158static
159void RunEpicTest()
160{
161 int b;
162 atomicValue v;
163
164 SDL_Log("\nepic test---------------------------------------\n\n");
165
166 SDL_Log("Size asserted to be >= 32-bit\n");
167 SDL_assert(sizeof(atomicValue)>=4);
168
169 SDL_Log("Check static initializer\n");
170 v=SDL_AtomicGet(&good);
171 SDL_assert(v==42);
172
173 SDL_assert(bad==42);
174
175 SDL_Log("Test negative values\n");
176 SDL_AtomicSet(&good, -5);
177 v=SDL_AtomicGet(&good);
178 SDL_assert(v==-5);
179
180 SDL_Log("Verify maximum value\n");
181 SDL_AtomicSet(&good, CountTo);
182 v=SDL_AtomicGet(&good);
183 SDL_assert(v==CountTo);
184
185 SDL_Log("Test compare and exchange\n");
186
187 b=SDL_AtomicCAS(&good, 500, 43);
188 SDL_assert(!b); /* no swap since CountTo!=500 */
189 v=SDL_AtomicGet(&good);
190 SDL_assert(v==CountTo); /* ensure no swap */
191
192 b=SDL_AtomicCAS(&good, CountTo, 44);
193 SDL_assert(!!b); /* will swap */
194 v=SDL_AtomicGet(&good);
195 SDL_assert(v==44);
196
197 SDL_Log("Test Add\n");
198
199 v=SDL_AtomicAdd(&good, 1);
200 SDL_assert(v==44);
201 v=SDL_AtomicGet(&good);
202 SDL_assert(v==45);
203
204 v=SDL_AtomicAdd(&good, 10);
205 SDL_assert(v==45);
206 v=SDL_AtomicGet(&good);
207 SDL_assert(v==55);
208
209 SDL_Log("Test Add (Negative values)\n");
210
211 v=SDL_AtomicAdd(&good, -20);
212 SDL_assert(v==55);
213 v=SDL_AtomicGet(&good);
214 SDL_assert(v==35);
215
216 v=SDL_AtomicAdd(&good, -50); /* crossing zero down */
217 SDL_assert(v==35);
218 v=SDL_AtomicGet(&good);
219 SDL_assert(v==-15);
220
221 v=SDL_AtomicAdd(&good, 30); /* crossing zero up */
222 SDL_assert(v==-15);
223 v=SDL_AtomicGet(&good);
224 SDL_assert(v==15);
225
226 SDL_Log("Reset before count down test\n");
227 SDL_AtomicSet(&good, CountTo);
228 v=SDL_AtomicGet(&good);
229 SDL_assert(v==CountTo);
230
231 bad=CountTo;
232 SDL_assert(bad==CountTo);
233
234 SDL_Log("Counting down from %d, Expect %d remaining\n",CountTo,Expect);
235 runAdder();
236
237 v=SDL_AtomicGet(&good);
238 SDL_Log("Atomic %d Non-Atomic %d\n",v,bad);
239 SDL_assert(v==Expect);
240 SDL_assert(bad!=Expect);
241}
242
243/* End atomic operation test */
244/**************************************************************************/
245
246/**************************************************************************/
247/* Lock-free FIFO test */
248
249/* This is useful to test the impact of another thread locking the queue
250 entirely for heavy-weight manipulation.
251 */
252#define TEST_SPINLOCK_FIFO
253
254#define NUM_READERS 4
255#define NUM_WRITERS 4
256#define EVENTS_PER_WRITER 1000000
257
258/* The number of entries must be a power of 2 */
259#define MAX_ENTRIES 256
260#define WRAP_MASK (MAX_ENTRIES-1)
261
262typedef struct
263{
264 SDL_atomic_t sequence;
265 SDL_Event event;
266} SDL_EventQueueEntry;
267
268typedef struct
269{
270 SDL_EventQueueEntry entries[MAX_ENTRIES];
271
272 char cache_pad1[SDL_CACHELINE_SIZE-((sizeof(SDL_EventQueueEntry)*MAX_ENTRIES)%SDL_CACHELINE_SIZE)];
273
274 SDL_atomic_t enqueue_pos;
275
276 char cache_pad2[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
277
278 SDL_atomic_t dequeue_pos;
279
280 char cache_pad3[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
281
282#ifdef TEST_SPINLOCK_FIFO
283 SDL_SpinLock lock;
284 SDL_atomic_t rwcount;
285 SDL_atomic_t watcher;
286
287 char cache_pad4[SDL_CACHELINE_SIZE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)];
288#endif
289
290 SDL_atomic_t active;
291
292 /* Only needed for the mutex test */
293 SDL_mutex *mutex;
294
295} SDL_EventQueue;
296
297static void InitEventQueue(SDL_EventQueue *queue)
298{
299 int i;
300
301 for (i = 0; i < MAX_ENTRIES; ++i) {
302 SDL_AtomicSet(&queue->entries[i].sequence, i);
303 }
304 SDL_AtomicSet(&queue->enqueue_pos, 0);
305 SDL_AtomicSet(&queue->dequeue_pos, 0);
306#ifdef TEST_SPINLOCK_FIFO
307 queue->lock = 0;
308 SDL_AtomicSet(&queue->rwcount, 0);
309 SDL_AtomicSet(&queue->watcher, 0);
310#endif
311 SDL_AtomicSet(&queue->active, 1);
312}
313
314static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
315{
316 SDL_EventQueueEntry *entry;
317 unsigned queue_pos;
318 unsigned entry_seq;
319 int delta;
320 SDL_bool status;
321
322#ifdef TEST_SPINLOCK_FIFO
323 /* This is a gate so an external thread can lock the queue */
324 SDL_AtomicLock(&queue->lock);
325 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
326 SDL_AtomicIncRef(&queue->rwcount);
327 SDL_AtomicUnlock(&queue->lock);
328#endif
329
330 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
331 for ( ; ; ) {
332 entry = &queue->entries[queue_pos & WRAP_MASK];
333 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
334
335 delta = (int)(entry_seq - queue_pos);
336 if (delta == 0) {
337 /* The entry and the queue position match, try to increment the queue position */
338 if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
339 /* We own the object, fill it! */
340 entry->event = *event;
341 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
342 status = SDL_TRUE;
343 break;
344 }
345 } else if (delta < 0) {
346 /* We ran into an old queue entry, which means it still needs to be dequeued */
347 status = SDL_FALSE;
348 break;
349 } else {
350 /* We ran into a new queue entry, get the new queue position */
351 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
352 }
353 }
354
355#ifdef TEST_SPINLOCK_FIFO
356 (void) SDL_AtomicDecRef(&queue->rwcount);
357#endif
358 return status;
359}
360
361static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
362{
363 SDL_EventQueueEntry *entry;
364 unsigned queue_pos;
365 unsigned entry_seq;
366 int delta;
367 SDL_bool status;
368
369#ifdef TEST_SPINLOCK_FIFO
370 /* This is a gate so an external thread can lock the queue */
371 SDL_AtomicLock(&queue->lock);
372 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
373 SDL_AtomicIncRef(&queue->rwcount);
374 SDL_AtomicUnlock(&queue->lock);
375#endif
376
377 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
378 for ( ; ; ) {
379 entry = &queue->entries[queue_pos & WRAP_MASK];
380 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
381
382 delta = (int)(entry_seq - (queue_pos + 1));
383 if (delta == 0) {
384 /* The entry and the queue position match, try to increment the queue position */
385 if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
386 /* We own the object, fill it! */
387 *event = entry->event;
388 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
389 status = SDL_TRUE;
390 break;
391 }
392 } else if (delta < 0) {
393 /* We ran into an old queue entry, which means we've hit empty */
394 status = SDL_FALSE;
395 break;
396 } else {
397 /* We ran into a new queue entry, get the new queue position */
398 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
399 }
400 }
401
402#ifdef TEST_SPINLOCK_FIFO
403 (void) SDL_AtomicDecRef(&queue->rwcount);
404#endif
405 return status;
406}
407
408static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
409{
410 SDL_EventQueueEntry *entry;
411 unsigned queue_pos;
412 unsigned entry_seq;
413 int delta;
414 SDL_bool status = SDL_FALSE;
415
416 SDL_LockMutex(queue->mutex);
417
418 queue_pos = (unsigned)queue->enqueue_pos.value;
419 entry = &queue->entries[queue_pos & WRAP_MASK];
420 entry_seq = (unsigned)entry->sequence.value;
421
422 delta = (int)(entry_seq - queue_pos);
423 if (delta == 0) {
424 ++queue->enqueue_pos.value;
425
426 /* We own the object, fill it! */
427 entry->event = *event;
428 entry->sequence.value = (int)(queue_pos + 1);
429 status = SDL_TRUE;
430 } else if (delta < 0) {
431 /* We ran into an old queue entry, which means it still needs to be dequeued */
432 } else {
433 SDL_Log("ERROR: mutex failed!\n");
434 }
435
436 SDL_UnlockMutex(queue->mutex);
437
438 return status;
439}
440
441static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
442{
443 SDL_EventQueueEntry *entry;
444 unsigned queue_pos;
445 unsigned entry_seq;
446 int delta;
447 SDL_bool status = SDL_FALSE;
448
449 SDL_LockMutex(queue->mutex);
450
451 queue_pos = (unsigned)queue->dequeue_pos.value;
452 entry = &queue->entries[queue_pos & WRAP_MASK];
453 entry_seq = (unsigned)entry->sequence.value;
454
455 delta = (int)(entry_seq - (queue_pos + 1));
456 if (delta == 0) {
457 ++queue->dequeue_pos.value;
458
459 /* We own the object, fill it! */
460 *event = entry->event;
461 entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
462 status = SDL_TRUE;
463 } else if (delta < 0) {
464 /* We ran into an old queue entry, which means we've hit empty */
465 } else {
466 SDL_Log("ERROR: mutex failed!\n");
467 }
468
469 SDL_UnlockMutex(queue->mutex);
470
471 return status;
472}
473
474static SDL_sem *writersDone;
475static SDL_sem *readersDone;
476
477typedef struct
478{
479 SDL_EventQueue *queue;
480 int index;
481 char padding1[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int))%SDL_CACHELINE_SIZE];
482 int waits;
483 SDL_bool lock_free;
484 char padding2[SDL_CACHELINE_SIZE-sizeof(int)-sizeof(SDL_bool)];
485} WriterData;
486
487typedef struct
488{
489 SDL_EventQueue *queue;
490 int counters[NUM_WRITERS];
491 int waits;
492 SDL_bool lock_free;
493 char padding[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int)*NUM_WRITERS+sizeof(int)+sizeof(SDL_bool))%SDL_CACHELINE_SIZE];
494} ReaderData;
495
496static int SDLCALL FIFO_Writer(void* _data)
497{
498 WriterData *data = (WriterData *)_data;
499 SDL_EventQueue *queue = data->queue;
500 int i;
501 SDL_Event event;
502
503 event.type = SDL_USEREVENT;
504 event.user.windowID = 0;
505 event.user.code = 0;
506 event.user.data1 = data;
507 event.user.data2 = NULL;
508
509 if (data->lock_free) {
510 for (i = 0; i < EVENTS_PER_WRITER; ++i) {
511 event.user.code = i;
512 while (!EnqueueEvent_LockFree(queue, &event)) {
513 ++data->waits;
514 SDL_Delay(0);
515 }
516 }
517 } else {
518 for (i = 0; i < EVENTS_PER_WRITER; ++i) {
519 event.user.code = i;
520 while (!EnqueueEvent_Mutex(queue, &event)) {
521 ++data->waits;
522 SDL_Delay(0);
523 }
524 }
525 }
526 SDL_SemPost(writersDone);
527 return 0;
528}
529
530static int SDLCALL 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_SemPost(readersDone);
564 return 0;
565}
566
567#ifdef TEST_SPINLOCK_FIFO
568/* This thread periodically locks the queue for no particular reason */
569static int SDLCALL FIFO_Watcher(void* _data)
570{
571 SDL_EventQueue *queue = (SDL_EventQueue *)_data;
572
573 while (SDL_AtomicGet(&queue->active)) {
574 SDL_AtomicLock(&queue->lock);
575 SDL_AtomicIncRef(&queue->watcher);
576 while (SDL_AtomicGet(&queue->rwcount) > 0) {
577 SDL_Delay(0);
578 }
579 /* Do queue manipulation here... */
580 (void) SDL_AtomicDecRef(&queue->watcher);
581 SDL_AtomicUnlock(&queue->lock);
582
583 /* Wait a bit... */
584 SDL_Delay(1);
585 }
586 return 0;
587}
588#endif /* TEST_SPINLOCK_FIFO */
589
590static void RunFIFOTest(SDL_bool lock_free)
591{
592 SDL_EventQueue queue;
593 WriterData writerData[NUM_WRITERS];
594 ReaderData readerData[NUM_READERS];
595 Uint32 start, end;
596 int i, j;
597 int grand_total;
598 char textBuffer[1024];
599 size_t len;
600
601 SDL_Log("\nFIFO test---------------------------------------\n\n");
602 SDL_Log("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
603
604 readersDone = SDL_CreateSemaphore(0);
605 writersDone = SDL_CreateSemaphore(0);
606
607 SDL_memset(&queue, 0xff, sizeof(queue));
608
609 InitEventQueue(&queue);
610 if (!lock_free) {
611 queue.mutex = SDL_CreateMutex();
612 }
613
614 start = SDL_GetTicks();
615
616#ifdef TEST_SPINLOCK_FIFO
617 /* Start a monitoring thread */
618 if (lock_free) {
619 SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
620 }
621#endif
622
623 /* Start the readers first */
624 SDL_Log("Starting %d readers\n", NUM_READERS);
625 SDL_zeroa(readerData);
626 for (i = 0; i < NUM_READERS; ++i) {
627 char name[64];
628 SDL_snprintf(name, sizeof (name), "FIFOReader%d", i);
629 readerData[i].queue = &queue;
630 readerData[i].lock_free = lock_free;
631 SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
632 }
633
634 /* Start up the writers */
635 SDL_Log("Starting %d writers\n", NUM_WRITERS);
636 SDL_zeroa(writerData);
637 for (i = 0; i < NUM_WRITERS; ++i) {
638 char name[64];
639 SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i);
640 writerData[i].queue = &queue;
641 writerData[i].index = i;
642 writerData[i].lock_free = lock_free;
643 SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
644 }
645
646 /* Wait for the writers */
647 for (i = 0; i < NUM_WRITERS; ++i) {
648 SDL_SemWait(writersDone);
649 }
650
651 /* Shut down the queue so readers exit */
652 SDL_AtomicSet(&queue.active, 0);
653
654 /* Wait for the readers */
655 for (i = 0; i < NUM_READERS; ++i) {
656 SDL_SemWait(readersDone);
657 }
658
659 end = SDL_GetTicks();
660
661 SDL_DestroySemaphore(readersDone);
662 SDL_DestroySemaphore(writersDone);
663
664 if (!lock_free) {
665 SDL_DestroyMutex(queue.mutex);
666 }
667
668 SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
669
670 SDL_Log("\n");
671 for (i = 0; i < NUM_WRITERS; ++i) {
672 SDL_Log("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
673 }
674 SDL_Log("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
675
676 /* Print a breakdown of which readers read messages from which writer */
677 SDL_Log("\n");
678 grand_total = 0;
679 for (i = 0; i < NUM_READERS; ++i) {
680 int total = 0;
681 for (j = 0; j < NUM_WRITERS; ++j) {
682 total += readerData[i].counters[j];
683 }
684 grand_total += total;
685 SDL_Log("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
686 SDL_snprintf(textBuffer, sizeof(textBuffer), " { ");
687 for (j = 0; j < NUM_WRITERS; ++j) {
688 if (j > 0) {
689 len = SDL_strlen(textBuffer);
690 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
691 }
692 len = SDL_strlen(textBuffer);
693 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]);
694 }
695 len = SDL_strlen(textBuffer);
696 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n");
697 SDL_Log("%s", textBuffer);
698 }
699 SDL_Log("Readers read %d total events\n", grand_total);
700}
701
702/* End FIFO test */
703/**************************************************************************/
704
705int
706main(int argc, char *argv[])
707{
708 /* Enable standard application logging */
709 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
710
711 RunBasicTest();
712 RunEpicTest();
713/* This test is really slow, so don't run it by default */
714#if 0
715 RunFIFOTest(SDL_FALSE);
716#endif
717 RunFIFOTest(SDL_TRUE);
718 return 0;
719}
720
721/* vi: set ts=4 sw=4 expandtab: */
722