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