void update() { //Calculate time step float timeStep = stepTimer.getTicks()/1000.f; //pseudo: //update any world stuff (background objects, etc) //NPC AI //for each npc //npc.strategize //for each object in the world //object.framenumber++ //object.move (x += xv, y += yv) //handle collisions //Move for time step stepTimer.start(); //Calculate FPS timeText.str(""); SDL_AtomicLock( &gDataLock ); timeText << "FPS: " << framesThisSecond; SDL_AtomicUnlock( &gDataLock ); if(!gFPSTextTexture.loadFromRenderedText(timeText.str().c_str(), fpsColor)) { printf("Unable to render FPS texture.\n"); } }
int fpsworker( void* data ) { //Work continuously SDL_AtomicLock( &gDataLock ); bool fpsquit = quit; SDL_AtomicUnlock( &gDataLock ); while(!fpsquit) { SDL_AtomicLock( &gDataLock ); framesThisSecond = countedFrames; countedFrames = 0; SDL_AtomicUnlock( &gDataLock ); //Wait one second SDL_Delay(1000); } return 0; }
/* Routine to get the thread-specific error variable */ SDL_error * SDL_GetErrBuf(void) { static SDL_SpinLock tls_lock; static SDL_bool tls_being_created; static SDL_TLSID tls_errbuf; static SDL_error SDL_global_errbuf; const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1; SDL_error *errbuf; /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails. It also means it's possible for another thread to also use SDL_global_errbuf, but that's very unlikely and hopefully won't cause issues. */ if (!tls_errbuf && !tls_being_created) { SDL_AtomicLock(&tls_lock); if (!tls_errbuf) { SDL_TLSID slot; tls_being_created = SDL_TRUE; slot = SDL_TLSCreate(); tls_being_created = SDL_FALSE; SDL_MemoryBarrierRelease(); tls_errbuf = slot; } SDL_AtomicUnlock(&tls_lock); } if (!tls_errbuf) { return &SDL_global_errbuf; } SDL_MemoryBarrierAcquire(); errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf); if (errbuf == ALLOCATION_IN_PROGRESS) { return &SDL_global_errbuf; } if (!errbuf) { /* Mark that we're in the middle of allocating our buffer */ SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL); errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf)); if (!errbuf) { SDL_TLSSet(tls_errbuf, NULL, NULL); return &SDL_global_errbuf; } SDL_zerop(errbuf); SDL_TLSSet(tls_errbuf, errbuf, SDL_free); } return errbuf; }
static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event) { SDL_EventQueueEntry *entry; unsigned queue_pos; unsigned entry_seq; int delta; SDL_bool status; #ifdef TEST_SPINLOCK_FIFO /* This is a gate so an external thread can lock the queue */ SDL_AtomicLock(&queue->lock); SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); SDL_AtomicIncRef(&queue->rwcount); SDL_AtomicUnlock(&queue->lock); #endif queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); for ( ; ; ) { entry = &queue->entries[queue_pos & WRAP_MASK]; entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); delta = (int)(entry_seq - (queue_pos + 1)); if (delta == 0) { /* The entry and the queue position match, try to increment the queue position */ if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) { /* We own the object, fill it! */ *event = entry->event; SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES)); status = SDL_TRUE; break; } } else if (delta < 0) { /* We ran into an old queue entry, which means we've hit empty */ status = SDL_FALSE; break; } else { /* We ran into a new queue entry, get the new queue position */ queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); } } #ifdef TEST_SPINLOCK_FIFO SDL_AtomicDecRef(&queue->rwcount); #endif return status; }
void processInput(SDL_Event e) { while(SDL_PollEvent(&e) != 0) { if(e.type == SDL_QUIT) { SDL_AtomicLock( &gDataLock ); quit = true; SDL_AtomicUnlock( &gDataLock ); } } const Uint8* currentKeyStates = SDL_GetKeyboardState(NULL); if(currentKeyStates[SDL_SCANCODE_UP]) { //do something } }
/* This thread periodically locks the queue for no particular reason */ static int FIFO_Watcher(void* _data) { SDL_EventQueue *queue = (SDL_EventQueue *)_data; while (queue->active) { SDL_AtomicLock(&queue->lock); SDL_AtomicIncRef(&queue->watcher); while (SDL_AtomicGet(&queue->rwcount) > 0) { SDL_Delay(0); } /* Do queue manipulation here... */ SDL_AtomicDecRef(&queue->watcher); SDL_AtomicUnlock(&queue->lock); /* Wait a bit... */ SDL_Delay(1); } return 0; }
static void RunBasicTest() { int value; SDL_SpinLock lock = 0; SDL_atomic_t v; SDL_bool tfret = SDL_FALSE; printf("\nspin lock---------------------------------------\n\n"); SDL_AtomicLock(&lock); printf("AtomicLock lock=%d\n", lock); SDL_AtomicUnlock(&lock); printf("AtomicUnlock lock=%d\n", lock); printf("\natomic -----------------------------------------\n\n"); SDL_AtomicSet(&v, 0); tfret = SDL_AtomicSet(&v, 10) == 0; printf("AtomicSet(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); tfret = SDL_AtomicAdd(&v, 10) == 10; printf("AtomicAdd(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); SDL_AtomicSet(&v, 0); SDL_AtomicIncRef(&v); tfret = (SDL_AtomicGet(&v) == 1); printf("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); SDL_AtomicIncRef(&v); tfret = (SDL_AtomicGet(&v) == 2); printf("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE); printf("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE); printf("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); SDL_AtomicSet(&v, 10); tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE); printf("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); value = SDL_AtomicGet(&v); tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE); printf("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); }
void render() { //Clear screen SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(gRenderer); //draw background //draw background objects //draw bullets //draw npcs //draw player //draw asteroids/explosions/effects //draw FPS number (framesThisSecond) gFPSTextTexture.render((SCREEN_WIDTH - gFPSTextTexture.getWidth())/2, (SCREEN_HEIGHT - gFPSTextTexture.getHeight())/2); //flip SDL_RenderPresent(gRenderer); //Increment frame counter SDL_AtomicLock( &gDataLock ); countedFrames++; SDL_AtomicUnlock( &gDataLock ); }
SDL_TLSData * SDL_Generic_GetTLSData() { SDL_threadID thread = SDL_ThreadID(); SDL_TLSEntry *entry; SDL_TLSData *storage = NULL; #if !SDL_THREADS_DISABLED if (!SDL_generic_TLS_mutex) { static SDL_SpinLock tls_lock; SDL_AtomicLock(&tls_lock); if (!SDL_generic_TLS_mutex) { SDL_mutex *mutex = SDL_CreateMutex(); SDL_MemoryBarrierRelease(); SDL_generic_TLS_mutex = mutex; if (!SDL_generic_TLS_mutex) { SDL_AtomicUnlock(&tls_lock); return NULL; } } SDL_AtomicUnlock(&tls_lock); } #endif /* SDL_THREADS_DISABLED */ SDL_MemoryBarrierAcquire(); SDL_LockMutex(SDL_generic_TLS_mutex); for (entry = SDL_generic_TLS; entry; entry = entry->next) { if (entry->thread == thread) { storage = entry->storage; break; } } #if !SDL_THREADS_DISABLED SDL_UnlockMutex(SDL_generic_TLS_mutex); #endif return storage; }
SDL_TLSData * SDL_SYS_GetTLSData() { if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) { static SDL_SpinLock lock; SDL_AtomicLock(&lock); if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) { DWORD storage = TlsAlloc(); if (storage != TLS_OUT_OF_INDEXES) { SDL_MemoryBarrierRelease(); thread_local_storage = storage; } else { generic_local_storage = SDL_TRUE; } } SDL_AtomicUnlock(&lock); } if (generic_local_storage) { return SDL_Generic_GetTLSData(); } SDL_MemoryBarrierAcquire(); return (SDL_TLSData *)TlsGetValue(thread_local_storage); }
SDL_TLSData * SDL_SYS_GetTLSData() { if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) { static SDL_SpinLock lock; SDL_AtomicLock(&lock); if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) { pthread_key_t storage; if (pthread_key_create(&storage, NULL) == 0) { SDL_MemoryBarrierRelease(); thread_local_storage = storage; } else { generic_local_storage = SDL_TRUE; } } SDL_AtomicUnlock(&lock); } if (generic_local_storage) { return SDL_Generic_GetTLSData(); } SDL_MemoryBarrierAcquire(); return (SDL_TLSData *)pthread_getspecific(thread_local_storage); }
int worker( void* data ) { printf( "%s starting...\n", data ); //Pre thread random seeding srand( SDL_GetTicks() ); //Work 5 times for( int i = 0; i < 5; ++i ) { //Wait randomly SDL_Delay( 16 + rand() % 32 ); //Lock SDL_AtomicLock( &gDataLock ); //Print pre work data printf( "%s gets %d\n", data, gData ); //"Work" gData = rand() % 256; //Print post work data printf( "%s sets %d\n\n", data, gData ); //Unlock SDL_AtomicUnlock( &gDataLock ); //Wait randomly SDL_Delay( 16 + rand() % 640 ); } printf( "%s finished!\n\n", data ); return 0; }
SDL_assert_state SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, int line) { static int assertion_running = 0; static SDL_SpinLock spinlock = 0; SDL_assert_state state = SDL_ASSERTION_IGNORE; SDL_AtomicLock(&spinlock); if (assertion_mutex == NULL) { /* never called SDL_Init()? */ assertion_mutex = SDL_CreateMutex(); if (assertion_mutex == NULL) { SDL_AtomicUnlock(&spinlock); return SDL_ASSERTION_IGNORE; /* oh well, I guess. */ } } SDL_AtomicUnlock(&spinlock); if (SDL_LockMutex(assertion_mutex) < 0) { return SDL_ASSERTION_IGNORE; /* oh well, I guess. */ } /* doing this because Visual C is upset over assigning in the macro. */ if (data->trigger_count == 0) { data->function = func; data->filename = file; data->linenum = line; } SDL_AddAssertionToReport(data); assertion_running++; if (assertion_running > 1) { /* assert during assert! Abort. */ if (assertion_running == 2) { SDL_AbortAssertion(); } else if (assertion_running == 3) { /* Abort asserted! */ SDL_ExitProcess(42); } else { while (1) { /* do nothing but spin; what else can you do?! */ } } } if (!data->always_ignore) { state = assertion_handler(data, assertion_userdata); } switch (state) { case SDL_ASSERTION_ABORT: SDL_AbortAssertion(); return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */ case SDL_ASSERTION_ALWAYS_IGNORE: state = SDL_ASSERTION_IGNORE; data->always_ignore = 1; break; case SDL_ASSERTION_IGNORE: case SDL_ASSERTION_RETRY: case SDL_ASSERTION_BREAK: break; /* macro handles these. */ } assertion_running--; SDL_UnlockMutex(assertion_mutex); return state; }
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param) { SDL_TimerData *data = &SDL_timer_data; SDL_Timer *timer; SDL_TimerMap *entry; if (!data->active) { int status = 0; SDL_AtomicLock(&data->lock); if (!data->active) { status = SDL_TimerInit(); } SDL_AtomicUnlock(&data->lock); if (status < 0) { return 0; } } SDL_AtomicLock(&data->lock); timer = data->freelist; if (timer) { data->freelist = timer->next; } SDL_AtomicUnlock(&data->lock); if (timer) { SDL_RemoveTimer(timer->timerID); } else { timer = (SDL_Timer *)SDL_malloc(sizeof(*timer)); if (!timer) { SDL_OutOfMemory(); return 0; } } timer->timerID = SDL_AtomicIncRef(&data->nextID); timer->callback = callback; timer->param = param; timer->interval = interval; timer->scheduled = SDL_GetTicks() + interval; timer->canceled = SDL_FALSE; entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry)); if (!entry) { SDL_free(timer); SDL_OutOfMemory(); return 0; } entry->timer = timer; entry->timerID = timer->timerID; SDL_mutexP(data->timermap_lock); entry->next = data->timermap; data->timermap = entry; SDL_mutexV(data->timermap_lock); /* Add the timer to the pending list for the timer thread */ SDL_AtomicLock(&data->lock); timer->next = data->pending; data->pending = timer; SDL_AtomicUnlock(&data->lock); /* Wake up the timer thread if necessary */ SDL_SemPost(data->sem); return entry->timerID; }
static int SDL_TimerThread(void *_data) { SDL_TimerData *data = (SDL_TimerData *)_data; SDL_Timer *pending; SDL_Timer *current; SDL_Timer *freelist_head = NULL; SDL_Timer *freelist_tail = NULL; Uint32 tick, now, interval, delay; /* Threaded timer loop: * 1. Queue timers added by other threads * 2. Handle any timers that should dispatch this cycle * 3. Wait until next dispatch time or new timer arrives */ for ( ; ; ) { /* Pending and freelist maintenance */ SDL_AtomicLock(&data->lock); { /* Get any timers ready to be queued */ pending = data->pending; data->pending = NULL; /* Make any unused timer structures available */ if (freelist_head) { freelist_tail->next = data->freelist; data->freelist = freelist_head; } } SDL_AtomicUnlock(&data->lock); /* Sort the pending timers into our list */ while (pending) { current = pending; pending = pending->next; SDL_AddTimerInternal(data, current); } freelist_head = NULL; freelist_tail = NULL; /* Check to see if we're still running, after maintenance */ if (!data->active) { break; } /* Initial delay if there are no timers */ delay = SDL_MUTEX_MAXWAIT; tick = SDL_GetTicks(); /* Process all the pending timers for this tick */ while (data->timers) { current = data->timers; if ((Sint32)(tick-current->scheduled) < 0) { /* Scheduled for the future, wait a bit */ delay = (current->scheduled - tick); break; } /* We're going to do something with this timer */ data->timers = current->next; if (current->canceled) { interval = 0; } else { interval = current->callback(current->interval, current->param); } if (interval > 0) { /* Reschedule this timer */ current->scheduled = tick + interval; SDL_AddTimerInternal(data, current); } else { if (!freelist_head) { freelist_head = current; } if (freelist_tail) { freelist_tail->next = current; } freelist_tail = current; current->canceled = SDL_TRUE; } } /* Adjust the delay based on processing time */ now = SDL_GetTicks(); interval = (now - tick); if (interval > delay) { delay = 0; } else { delay -= interval; } /* Note that each time a timer is added, this will return immediately, but we process the timers added all at once. That's okay, it just means we run through the loop a few extra times. */ SDL_SemWaitTimeout(data->sem, delay); } return 0; }
void fe_mt_spinlock_lock (fe_mt_spinlock *spinlock) { SDL_AtomicLock(spinlock); }
int main( int argc, char* args[] ) { //The window being rendered to SDL_Window* window = NULL; //THe surface contained by the window SDL_Surface* screenSurface = NULL; //Initialize SDL if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() ); } else { //Create window window = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN ); if( window == NULL) { printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() ); } else { //Get window surface screenSurface= SDL_GetWindowSurface( window ); //Fill the surface white SDL_FillRect( screenSurface, NULL, SDL_MapRGB( screenSurface->format, 0xFF, 0xFF, 0xFF ) ); //Update the surface SDL_UpdateWindowSurface( window ); //Wait 2 seconds SDL_Delay(2000); } } quit = false; countedFrames = 0; framesThisSecond = 0; //Start fps counting thread SDL_Thread* fpsThread = SDL_CreateThread(fpsworker, "FPSThread", NULL); //FPS text std::stringstream timeText; SDL_Color fpsColor = {0,0,0,255}; #MAIN GAME LOOP SDL_AtomicLock( &gDataLock ); bool mainquit = quit; SDL_AtomicUnlock( &gDataLock ); while(!mainquit) { //Get/process input from user processInput(); //Make all input-independent updates (game world, physics, AI, etc) update(); //Draw world + objects render(); //Delay (goal: 120 fps with vsync) delay(); } SDL_WaitThread(fpsThread, NULL); //Destroy window SDL_DestroyWindow( window ); //Quit SDL subsystems SDL_Quit(); return 0; }