Mercurial > sdl-ios-xcode
comparison test/testatomic.c @ 5101:1b3678ac9804
Added a test to measure the impact of a separate thread periodically locking the queue entirely.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Wed, 26 Jan 2011 00:03:34 -0800 |
parents | 470ede30189c |
children | 42a7591530d5 |
comparison
equal
deleted
inserted
replaced
5100:470ede30189c | 5101:1b3678ac9804 |
---|---|
232 /**************************************************************************/ | 232 /**************************************************************************/ |
233 | 233 |
234 /**************************************************************************/ | 234 /**************************************************************************/ |
235 /* Lock-free FIFO test */ | 235 /* Lock-free FIFO test */ |
236 | 236 |
237 /* This is useful to test the impact of another thread locking the queue | |
238 entirely for heavy-weight manipulation. | |
239 */ | |
240 #define TEST_SPINLOCK_FIFO | |
241 | |
237 #define NUM_READERS 4 | 242 #define NUM_READERS 4 |
238 #define NUM_WRITERS 4 | 243 #define NUM_WRITERS 4 |
239 #define EVENTS_PER_WRITER 1000000 | 244 #define EVENTS_PER_WRITER 1000000 |
240 | 245 |
241 /* A decent guess for the size of a cache line on this architecture */ | 246 /* A decent guess for the size of a cache line on this architecture */ |
263 | 268 |
264 SDL_atomic_t dequeue_pos; | 269 SDL_atomic_t dequeue_pos; |
265 | 270 |
266 char cache_pad3[CACHELINE-sizeof(SDL_atomic_t)]; | 271 char cache_pad3[CACHELINE-sizeof(SDL_atomic_t)]; |
267 | 272 |
273 #ifdef TEST_SPINLOCK_FIFO | |
274 SDL_SpinLock lock; | |
275 SDL_atomic_t rwcount; | |
276 SDL_atomic_t watcher; | |
277 | |
278 char cache_pad4[CACHELINE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)]; | |
279 #endif | |
280 | |
268 SDL_bool active; | 281 SDL_bool active; |
269 | 282 |
270 /* Only needed for the mutex test */ | 283 /* Only needed for the mutex test */ |
271 SDL_mutex *mutex; | 284 SDL_mutex *mutex; |
272 | 285 |
279 for (i = 0; i < MAX_ENTRIES; ++i) { | 292 for (i = 0; i < MAX_ENTRIES; ++i) { |
280 SDL_AtomicSet(&queue->entries[i].sequence, i); | 293 SDL_AtomicSet(&queue->entries[i].sequence, i); |
281 } | 294 } |
282 SDL_AtomicSet(&queue->enqueue_pos, 0); | 295 SDL_AtomicSet(&queue->enqueue_pos, 0); |
283 SDL_AtomicSet(&queue->dequeue_pos, 0); | 296 SDL_AtomicSet(&queue->dequeue_pos, 0); |
297 #ifdef TEST_SPINLOCK_FIFO | |
298 queue->lock = 0; | |
299 SDL_AtomicSet(&queue->rwcount, 0); | |
300 #endif | |
284 queue->active = SDL_TRUE; | 301 queue->active = SDL_TRUE; |
285 } | 302 } |
286 | 303 |
287 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event) | 304 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event) |
288 { | 305 { |
289 SDL_EventQueueEntry *entry; | 306 SDL_EventQueueEntry *entry; |
290 unsigned queue_pos; | 307 unsigned queue_pos; |
291 unsigned entry_seq; | 308 unsigned entry_seq; |
292 int delta; | 309 int delta; |
310 SDL_bool status; | |
311 | |
312 #ifdef TEST_SPINLOCK_FIFO | |
313 /* This is a gate so an external thread can lock the queue */ | |
314 SDL_AtomicLock(&queue->lock); | |
315 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); | |
316 SDL_AtomicIncRef(&queue->rwcount); | |
317 SDL_AtomicUnlock(&queue->lock); | |
318 #endif | |
293 | 319 |
294 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); | 320 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); |
295 for ( ; ; ) { | 321 for ( ; ; ) { |
296 entry = &queue->entries[queue_pos & WRAP_MASK]; | 322 entry = &queue->entries[queue_pos & WRAP_MASK]; |
297 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); | 323 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); |
298 | 324 |
299 delta = (int)(entry_seq - queue_pos); | 325 delta = (int)(entry_seq - queue_pos); |
300 if (delta == 0) { | 326 if (delta == 0) { |
301 /* The entry and the queue position match, try to increment the queue position */ | 327 /* The entry and the queue position match, try to increment the queue position */ |
302 if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) { | 328 if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) { |
329 /* We own the object, fill it! */ | |
330 entry->event = *event; | |
331 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1)); | |
332 status = SDL_TRUE; | |
303 break; | 333 break; |
304 } | 334 } |
305 } else if (delta < 0) { | 335 } else if (delta < 0) { |
306 /* We ran into an old queue entry, which means it still needs to be dequeued */ | 336 /* We ran into an old queue entry, which means it still needs to be dequeued */ |
307 return SDL_FALSE; | 337 status = SDL_FALSE; |
338 break; | |
308 } else { | 339 } else { |
309 /* We ran into a new queue entry, get the new queue position */ | 340 /* We ran into a new queue entry, get the new queue position */ |
310 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); | 341 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); |
311 } | 342 } |
312 } | 343 } |
313 | 344 |
314 /* We own the object, fill it! */ | 345 #ifdef TEST_SPINLOCK_FIFO |
315 entry->event = *event; | 346 SDL_AtomicDecRef(&queue->rwcount); |
316 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1)); | 347 #endif |
317 | 348 return status; |
318 return SDL_TRUE; | |
319 } | 349 } |
320 | 350 |
321 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event) | 351 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event) |
322 { | 352 { |
323 SDL_EventQueueEntry *entry; | 353 SDL_EventQueueEntry *entry; |
324 unsigned queue_pos; | 354 unsigned queue_pos; |
325 unsigned entry_seq; | 355 unsigned entry_seq; |
326 int delta; | 356 int delta; |
357 SDL_bool status; | |
358 | |
359 #ifdef TEST_SPINLOCK_FIFO | |
360 /* This is a gate so an external thread can lock the queue */ | |
361 SDL_AtomicLock(&queue->lock); | |
362 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); | |
363 SDL_AtomicIncRef(&queue->rwcount); | |
364 SDL_AtomicUnlock(&queue->lock); | |
365 #endif | |
327 | 366 |
328 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); | 367 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); |
329 for ( ; ; ) { | 368 for ( ; ; ) { |
330 entry = &queue->entries[queue_pos & WRAP_MASK]; | 369 entry = &queue->entries[queue_pos & WRAP_MASK]; |
331 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); | 370 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); |
332 | 371 |
333 delta = (int)(entry_seq - (queue_pos + 1)); | 372 delta = (int)(entry_seq - (queue_pos + 1)); |
334 if (delta == 0) { | 373 if (delta == 0) { |
335 /* The entry and the queue position match, try to increment the queue position */ | 374 /* The entry and the queue position match, try to increment the queue position */ |
336 if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) { | 375 if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) { |
376 /* We own the object, fill it! */ | |
377 *event = entry->event; | |
378 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES)); | |
379 status = SDL_TRUE; | |
337 break; | 380 break; |
338 } | 381 } |
339 } else if (delta < 0) { | 382 } else if (delta < 0) { |
340 /* We ran into an old queue entry, which means we've hit empty */ | 383 /* We ran into an old queue entry, which means we've hit empty */ |
341 return SDL_FALSE; | 384 status = SDL_FALSE; |
385 break; | |
342 } else { | 386 } else { |
343 /* We ran into a new queue entry, get the new queue position */ | 387 /* We ran into a new queue entry, get the new queue position */ |
344 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); | 388 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); |
345 } | 389 } |
346 } | 390 } |
347 | 391 |
348 /* We own the object, fill it! */ | 392 #ifdef TEST_SPINLOCK_FIFO |
349 *event = entry->event; | 393 SDL_AtomicDecRef(&queue->rwcount); |
350 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES)); | 394 #endif |
351 | 395 return status; |
352 return SDL_TRUE; | |
353 } | 396 } |
354 | 397 |
355 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event) | 398 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event) |
356 { | 399 { |
357 SDL_EventQueueEntry *entry; | 400 SDL_EventQueueEntry *entry; |
358 unsigned queue_pos; | 401 unsigned queue_pos; |
359 unsigned entry_seq; | 402 unsigned entry_seq; |
360 int delta; | 403 int delta; |
404 SDL_bool status = SDL_FALSE; | |
361 | 405 |
362 SDL_mutexP(queue->mutex); | 406 SDL_mutexP(queue->mutex); |
363 | 407 |
364 queue_pos = (unsigned)queue->enqueue_pos.value; | 408 queue_pos = (unsigned)queue->enqueue_pos.value; |
365 entry = &queue->entries[queue_pos & WRAP_MASK]; | 409 entry = &queue->entries[queue_pos & WRAP_MASK]; |
366 entry_seq = (unsigned)entry->sequence.value; | 410 entry_seq = (unsigned)entry->sequence.value; |
367 | 411 |
368 delta = (int)(entry_seq - queue_pos); | 412 delta = (int)(entry_seq - queue_pos); |
369 if (delta == 0) { | 413 if (delta == 0) { |
370 ++queue->enqueue_pos.value; | 414 ++queue->enqueue_pos.value; |
415 | |
416 /* We own the object, fill it! */ | |
417 entry->event = *event; | |
418 entry->sequence.value = (int)(queue_pos + 1); | |
419 status = SDL_TRUE; | |
371 } else if (delta < 0) { | 420 } else if (delta < 0) { |
372 /* We ran into an old queue entry, which means it still needs to be dequeued */ | 421 /* We ran into an old queue entry, which means it still needs to be dequeued */ |
373 SDL_mutexV(queue->mutex); | |
374 return SDL_FALSE; | |
375 } else { | 422 } else { |
376 printf("ERROR: mutex failed!\n"); | 423 printf("ERROR: mutex failed!\n"); |
377 } | 424 } |
378 | 425 |
379 /* We own the object, fill it! */ | |
380 entry->event = *event; | |
381 entry->sequence.value = (int)(queue_pos + 1); | |
382 | |
383 SDL_mutexV(queue->mutex); | 426 SDL_mutexV(queue->mutex); |
384 | 427 |
385 return SDL_TRUE; | 428 return status; |
386 } | 429 } |
387 | 430 |
388 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event) | 431 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event) |
389 { | 432 { |
390 SDL_EventQueueEntry *entry; | 433 SDL_EventQueueEntry *entry; |
391 unsigned queue_pos; | 434 unsigned queue_pos; |
392 unsigned entry_seq; | 435 unsigned entry_seq; |
393 int delta; | 436 int delta; |
437 SDL_bool status = SDL_FALSE; | |
394 | 438 |
395 SDL_mutexP(queue->mutex); | 439 SDL_mutexP(queue->mutex); |
396 | 440 |
397 queue_pos = (unsigned)queue->dequeue_pos.value; | 441 queue_pos = (unsigned)queue->dequeue_pos.value; |
398 entry = &queue->entries[queue_pos & WRAP_MASK]; | 442 entry = &queue->entries[queue_pos & WRAP_MASK]; |
399 entry_seq = (unsigned)entry->sequence.value; | 443 entry_seq = (unsigned)entry->sequence.value; |
400 | 444 |
401 delta = (int)(entry_seq - (queue_pos + 1)); | 445 delta = (int)(entry_seq - (queue_pos + 1)); |
402 if (delta == 0) { | 446 if (delta == 0) { |
403 ++queue->dequeue_pos.value; | 447 ++queue->dequeue_pos.value; |
448 | |
449 /* We own the object, fill it! */ | |
450 *event = entry->event; | |
451 entry->sequence.value = (int)(queue_pos + MAX_ENTRIES); | |
452 status = SDL_TRUE; | |
404 } else if (delta < 0) { | 453 } else if (delta < 0) { |
405 /* We ran into an old queue entry, which means we've hit empty */ | 454 /* We ran into an old queue entry, which means we've hit empty */ |
406 SDL_mutexV(queue->mutex); | |
407 return SDL_FALSE; | |
408 } else { | 455 } else { |
409 printf("ERROR: mutex failed!\n"); | 456 printf("ERROR: mutex failed!\n"); |
410 } | 457 } |
411 | 458 |
412 /* We own the object, fill it! */ | |
413 *event = entry->event; | |
414 entry->sequence.value = (int)(queue_pos + MAX_ENTRIES); | |
415 | |
416 SDL_mutexV(queue->mutex); | 459 SDL_mutexV(queue->mutex); |
417 | 460 |
418 return SDL_TRUE; | 461 return status; |
419 } | 462 } |
420 | 463 |
421 static SDL_sem *writersDone; | 464 static SDL_sem *writersDone; |
422 static SDL_sem *readersDone; | 465 static SDL_sem *readersDone; |
423 static SDL_atomic_t writersRunning; | 466 static SDL_atomic_t writersRunning; |
515 SDL_AtomicAdd(&readersRunning, -1); | 558 SDL_AtomicAdd(&readersRunning, -1); |
516 SDL_SemPost(readersDone); | 559 SDL_SemPost(readersDone); |
517 return 0; | 560 return 0; |
518 } | 561 } |
519 | 562 |
563 #ifdef TEST_SPINLOCK_FIFO | |
564 /* This thread periodically locks the queue for no particular reason */ | |
565 static int FIFO_Watcher(void* _data) | |
566 { | |
567 SDL_EventQueue *queue = (SDL_EventQueue *)_data; | |
568 | |
569 while (queue->active) { | |
570 SDL_AtomicLock(&queue->lock); | |
571 SDL_AtomicIncRef(&queue->watcher); | |
572 while (SDL_AtomicGet(&queue->rwcount) > 0) { | |
573 SDL_Delay(0); | |
574 } | |
575 /* Do queue manipulation here... */ | |
576 SDL_AtomicDecRef(&queue->watcher); | |
577 SDL_AtomicUnlock(&queue->lock); | |
578 | |
579 /* Wait a bit... */ | |
580 SDL_Delay(1); | |
581 } | |
582 return 0; | |
583 } | |
584 #endif /* TEST_SPINLOCK_FIFO */ | |
585 | |
520 static void RunFIFOTest(SDL_bool lock_free) | 586 static void RunFIFOTest(SDL_bool lock_free) |
521 { | 587 { |
522 SDL_EventQueue queue; | 588 SDL_EventQueue queue; |
523 WriterData writerData[NUM_WRITERS]; | 589 WriterData writerData[NUM_WRITERS]; |
524 ReaderData readerData[NUM_READERS]; | 590 ReaderData readerData[NUM_READERS]; |
539 queue.mutex = SDL_CreateMutex(); | 605 queue.mutex = SDL_CreateMutex(); |
540 } | 606 } |
541 | 607 |
542 start = SDL_GetTicks(); | 608 start = SDL_GetTicks(); |
543 | 609 |
610 #ifdef TEST_SPINLOCK_FIFO | |
611 /* Start a monitoring thread */ | |
612 if (lock_free) { | |
613 SDL_CreateThread(FIFO_Watcher, &queue); | |
614 } | |
615 #endif | |
616 | |
544 /* Start the readers first */ | 617 /* Start the readers first */ |
545 printf("Starting %d readers\n", NUM_READERS); | 618 printf("Starting %d readers\n", NUM_READERS); |
546 SDL_zero(readerData); | 619 SDL_zero(readerData); |
547 SDL_AtomicSet(&readersRunning, NUM_READERS); | 620 SDL_AtomicSet(&readersRunning, NUM_READERS); |
548 for (i = 0; i < NUM_READERS; ++i) { | 621 for (i = 0; i < NUM_READERS; ++i) { |