summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/timer/SDL_timer.c
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2016-02-27 16:28:05 +0100
committer Miodrag Milanovic <mmicko@gmail.com>2016-02-27 16:53:49 +0100
commitcf6dc7d370407914020f3e623719f09c8668c692 (patch)
tree46b3416d091b4c09b50fba7ffcbd310aed032cc8 /3rdparty/SDL2/src/timer/SDL_timer.c
parent64135e73f973d9b95beb0c60b65feb34fbfec64a (diff)
Placed SDL2 source since we need it on some platforms (nw)
Diffstat (limited to '3rdparty/SDL2/src/timer/SDL_timer.c')
-rw-r--r--3rdparty/SDL2/src/timer/SDL_timer.c389
1 files changed, 389 insertions, 0 deletions
diff --git a/3rdparty/SDL2/src/timer/SDL_timer.c b/3rdparty/SDL2/src/timer/SDL_timer.c
new file mode 100644
index 00000000000..6189ab8b508
--- /dev/null
+++ b/3rdparty/SDL2/src/timer/SDL_timer.c
@@ -0,0 +1,389 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../SDL_internal.h"
+
+#include "SDL_timer.h"
+#include "SDL_timer_c.h"
+#include "SDL_atomic.h"
+#include "SDL_cpuinfo.h"
+#include "SDL_thread.h"
+
+/* #define DEBUG_TIMERS */
+
+typedef struct _SDL_Timer
+{
+ int timerID;
+ SDL_TimerCallback callback;
+ void *param;
+ Uint32 interval;
+ Uint32 scheduled;
+ volatile SDL_bool canceled;
+ struct _SDL_Timer *next;
+} SDL_Timer;
+
+typedef struct _SDL_TimerMap
+{
+ int timerID;
+ SDL_Timer *timer;
+ struct _SDL_TimerMap *next;
+} SDL_TimerMap;
+
+/* The timers are kept in a sorted list */
+typedef struct {
+ /* Data used by the main thread */
+ SDL_Thread *thread;
+ SDL_atomic_t nextID;
+ SDL_TimerMap *timermap;
+ SDL_mutex *timermap_lock;
+
+ /* Padding to separate cache lines between threads */
+ char cache_pad[SDL_CACHELINE_SIZE];
+
+ /* Data used to communicate with the timer thread */
+ SDL_SpinLock lock;
+ SDL_sem *sem;
+ SDL_Timer * volatile pending;
+ SDL_Timer * volatile freelist;
+ volatile SDL_bool active;
+
+ /* List of timers - this is only touched by the timer thread */
+ SDL_Timer *timers;
+} SDL_TimerData;
+
+static SDL_TimerData SDL_timer_data;
+
+/* The idea here is that any thread might add a timer, but a single
+ * thread manages the active timer queue, sorted by scheduling time.
+ *
+ * Timers are removed by simply setting a canceled flag
+ */
+
+static void
+SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
+{
+ SDL_Timer *prev, *curr;
+
+ prev = NULL;
+ for (curr = data->timers; curr; prev = curr, curr = curr->next) {
+ if ((Sint32)(timer->scheduled-curr->scheduled) < 0) {
+ break;
+ }
+ }
+
+ /* Insert the timer here! */
+ if (prev) {
+ prev->next = timer;
+ } else {
+ data->timers = timer;
+ }
+ timer->next = curr;
+}
+
+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;
+}
+
+int
+SDL_TimerInit(void)
+{
+ SDL_TimerData *data = &SDL_timer_data;
+
+ if (!data->active) {
+ const char *name = "SDLTimer";
+ data->timermap_lock = SDL_CreateMutex();
+ if (!data->timermap_lock) {
+ return -1;
+ }
+
+ data->sem = SDL_CreateSemaphore(0);
+ if (!data->sem) {
+ SDL_DestroyMutex(data->timermap_lock);
+ return -1;
+ }
+
+ data->active = SDL_TRUE;
+ /* !!! FIXME: this is nasty. */
+#if defined(__WIN32__) && !defined(HAVE_LIBC)
+#undef SDL_CreateThread
+#if SDL_DYNAMIC_API
+ data->thread = SDL_CreateThread_REAL(SDL_TimerThread, name, data, NULL, NULL);
+#else
+ data->thread = SDL_CreateThread(SDL_TimerThread, name, data, NULL, NULL);
+#endif
+#else
+ data->thread = SDL_CreateThread(SDL_TimerThread, name, data);
+#endif
+ if (!data->thread) {
+ SDL_TimerQuit();
+ return -1;
+ }
+
+ SDL_AtomicSet(&data->nextID, 1);
+ }
+ return 0;
+}
+
+void
+SDL_TimerQuit(void)
+{
+ SDL_TimerData *data = &SDL_timer_data;
+ SDL_Timer *timer;
+ SDL_TimerMap *entry;
+
+ if (data->active) {
+ data->active = SDL_FALSE;
+
+ /* Shutdown the timer thread */
+ if (data->thread) {
+ SDL_SemPost(data->sem);
+ SDL_WaitThread(data->thread, NULL);
+ data->thread = NULL;
+ }
+
+ SDL_DestroySemaphore(data->sem);
+ data->sem = NULL;
+
+ /* Clean up the timer entries */
+ while (data->timers) {
+ timer = data->timers;
+ data->timers = timer->next;
+ SDL_free(timer);
+ }
+ while (data->freelist) {
+ timer = data->freelist;
+ data->freelist = timer->next;
+ SDL_free(timer);
+ }
+ while (data->timermap) {
+ entry = data->timermap;
+ data->timermap = entry->next;
+ SDL_free(entry);
+ }
+
+ SDL_DestroyMutex(data->timermap_lock);
+ data->timermap_lock = NULL;
+ }
+}
+
+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_LockMutex(data->timermap_lock);
+ entry->next = data->timermap;
+ data->timermap = entry;
+ SDL_UnlockMutex(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;
+}
+
+SDL_bool
+SDL_RemoveTimer(SDL_TimerID id)
+{
+ SDL_TimerData *data = &SDL_timer_data;
+ SDL_TimerMap *prev, *entry;
+ SDL_bool canceled = SDL_FALSE;
+
+ /* Find the timer */
+ SDL_LockMutex(data->timermap_lock);
+ prev = NULL;
+ for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
+ if (entry->timerID == id) {
+ if (prev) {
+ prev->next = entry->next;
+ } else {
+ data->timermap = entry->next;
+ }
+ break;
+ }
+ }
+ SDL_UnlockMutex(data->timermap_lock);
+
+ if (entry) {
+ if (!entry->timer->canceled) {
+ entry->timer->canceled = SDL_TRUE;
+ canceled = SDL_TRUE;
+ }
+ SDL_free(entry);
+ }
+ return canceled;
+}
+
+/* vi: set ts=4 sw=4 expandtab: */