]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
Split out TaskQueue into its own taskqueue.[ch] module, it doesn't depend on SDL2...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 18 Jan 2020 04:55:42 +0000 (04:55 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 18 Jan 2020 04:55:42 +0000 (04:55 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12495 d7cf8633-e32d-0410-b094-e92efae38249

darkplaces-sdl2-vs2017.vcxproj
host.c
r_shadow.h
sys_shared.c
thread.h
thread_sdl.c

index 69bf1fc0469100f7a4ac8a38c12f5f9d94a8ff4e..3d93832a9d5646c8d5e7dbac035c7b06c6d2bf96 100644 (file)
     <ClCompile Include="svvm_cmds.c" />\r
     <ClCompile Include="sys_sdl.c" />\r
     <ClCompile Include="sys_shared.c" />\r
+    <ClCompile Include="taskqueue.c" />\r
     <ClCompile Include="thread_sdl.c" />\r
     <ClCompile Include="utf8lib.c" />\r
     <ClCompile Include="vid_sdl.c" />\r
     <ClInclude Include="sv_demo.h" />\r
     <ClInclude Include="svbsp.h" />\r
     <ClInclude Include="sys.h" />\r
+    <ClInclude Include="taskqueue.h" />\r
     <ClInclude Include="thread.h" />\r
     <ClInclude Include="timing.h" />\r
     <ClInclude Include="utf8lib.h" />\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
   <ImportGroup Label="ExtensionTargets">\r
   </ImportGroup>\r
-</Project>\r
+</Project>
\ No newline at end of file
diff --git a/host.c b/host.c
index 61e84bc2e196a0b81c280416ef2950dc0635a320..2e9515a588b38859a7a19d4eaa077be8531f66be 100644 (file)
--- a/host.c
+++ b/host.c
@@ -29,6 +29,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "csprogs.h"
 #include "sv_demo.h"
 #include "snd_main.h"
+#include "taskqueue.h"
 #include "thread.h"
 #include "utf8lib.h"
 
index 778a44c4b07fdfd6b5ca31cda1bfa526e22a1cb1..25e6ce0f0126beef2d8bc33e68cf71b4e37ab0e8 100644 (file)
@@ -2,7 +2,7 @@
 #ifndef R_SHADOW_H
 #define R_SHADOW_H
 
-#include "thread.h"
+#include "taskqueue.h"
 
 #define R_SHADOW_SHADOWMAP_NUMCUBEMAPS 8
 
index 643d448514fb0f4b2c1d7f9610478a94583bdde3..92da4ec44888c08a97bd7c34a95b77918e3c0d62 100644 (file)
@@ -5,6 +5,7 @@
 #endif
 
 #include "quakedef.h"
+#include "taskqueue.h"
 #include "thread.h"
 
 #define SUPPORTDLL
index 85e5200140eb73d522a53c2ebcd24f5b8511dfbf..a9a00658bfc97f19ff7e715c9e91666c22042302 100644 (file)
--- a/thread.h
+++ b/thread.h
@@ -1,4 +1,5 @@
 #ifndef THREAD_H
+#define THREAD_H
 
 // enable Sys_PrintfToTerminal calls on nearly every threading call
 //#define THREADDEBUG
@@ -58,54 +59,4 @@ qboolean _Thread_AtomicTryLock(Thread_SpinLock *lock, const char *filename, int
 void _Thread_AtomicLock(Thread_SpinLock *lock, const char *filename, int fileline);
 void _Thread_AtomicUnlock(Thread_SpinLock *lock, const char *filename, int fileline);
 
-typedef struct taskqueue_task_s
-{
-       // doubly linked list
-       struct taskqueue_task_s * volatile prev;
-       struct taskqueue_task_s * volatile next;
-
-       // if not NULL, this task must be done before this one will dequeue (faster than simply Yielding immediately)
-       struct taskqueue_task_s *preceding;
-
-       // see TaskQueue_IsDone() to use proper atomics to poll done status
-       volatile int started;
-       volatile int done;
-
-       // function to call, and parameters for it to use
-       void(*func)(struct taskqueue_task_s *task);
-       void *p[4];
-       size_t i[4];
-
-       // stats:
-       unsigned int yieldcount; // number of times this task has been requeued
-}
-taskqueue_task_t;
-
-// immediately execute any pending tasks if threading is disabled (or if force is true)
-// TRY NOT TO USE THIS IF POSSIBLE - poll task->done instead.
-void TaskQueue_Execute(qboolean force);
-
-// queue the tasks to be executed, or executes them immediately if threading is disabled.
-void TaskQueue_Enqueue(int numtasks, taskqueue_task_t *tasks);
-
-// if the task can not be completed due yet to preconditions, just enqueue it again...
-void TaskQueue_Yield(taskqueue_task_t *t);
-
-// polls for status of task and returns the result immediately - use this instead of checking ->done directly, as this uses atomics
-qboolean TaskQueue_IsDone(taskqueue_task_t *t);
-
-// polls for status of task and waits for it to be done
-void TaskQueue_WaitForTaskDone(taskqueue_task_t *t);
-
-// updates thread count based on the cvar.
-void TaskQueue_Frame(qboolean shutdown);
-
-// convenience function for setting up a task structure.  Does not do the Enqueue, just fills in the struct.
-void TaskQueue_Setup(taskqueue_task_t *t, taskqueue_task_t *preceding, void(*func)(taskqueue_task_t *), size_t i0, size_t i1, void *p0, void *p1);
-
-// general purpose tasks
-// t->i[0] = number of tasks in array
-// t->p[0] = array of taskqueue_task_t to check
-void TaskQueue_Task_CheckTasksDone(taskqueue_task_t *t);
-
 #endif
index 8431ead2290365e411c064d5e057cbe101e2b1b3..f805e5403eae4f0ba27dd011d49d67231e5cc070 100644 (file)
@@ -3,57 +3,16 @@
 #include "quakedef.h"
 #include "thread.h"
 
-cvar_t taskqueue_maxthreads = {CVAR_SAVE, "taskqueue_maxthreads", "32", "how many threads to use for executing tasks"};
-cvar_t taskqueue_linkedlist = {CVAR_SAVE, "taskqueue_linkedlist", "1", "whether to use a doubly linked list or an array for the FIFO queue"};
-
-typedef struct taskqueue_state_thread_s
-{
-       void *handle;
-}
-taskqueue_state_thread_t;
-
-typedef struct taskqueue_state_s
-{
-       int numthreads;
-       taskqueue_state_thread_t threads[1024];
-
-       // we can enqueue this many tasks before execution of them must proceed
-       int queue_used;
-       int queue_max; // size of queue array
-       taskqueue_task_t **queue_tasks;
-
-       // command 
-       Thread_SpinLock command_lock;
-
-       volatile uint64_t threads_quit;
-
-       // doubly linked list - enqueue pushes to list.prev, dequeue pops from list.next
-       taskqueue_task_t list;
-}
-taskqueue_state_t;
-
-static taskqueue_state_t taskqueue_state;
-
 int Thread_Init(void)
 {
-       Cvar_RegisterVariable(&taskqueue_maxthreads);
-       Cvar_RegisterVariable(&taskqueue_linkedlist);
 #ifdef THREADDISABLE
        Con_Printf("Threading disabled in this build\n");
 #endif
-       // initialize the doubly-linked list header
-       taskqueue_state.list.next = &taskqueue_state.list;
-       taskqueue_state.list.prev = &taskqueue_state.list;
        return 0;
 }
 
 void Thread_Shutdown(void)
 {
-       if (taskqueue_state.numthreads)
-               TaskQueue_Frame(true);
-       if (taskqueue_state.queue_tasks)
-               Mem_Free(taskqueue_state.queue_tasks);
-       taskqueue_state.queue_tasks = NULL;
 }
 
 qboolean Thread_HasThreads(void)
@@ -272,192 +231,3 @@ void _Thread_AtomicUnlock(Thread_SpinLock *lock, const char *filename, int filel
 #endif
        SDL_AtomicUnlock(lock);
 }
-
-static taskqueue_task_t *TaskQueue_GetPending(void)
-{
-       taskqueue_task_t *t = NULL;
-       if (taskqueue_state.list.next != &taskqueue_state.list)
-       {
-               // pop from list.next
-               t = taskqueue_state.list.next;
-               t->next->prev = t->prev;
-               t->prev->next = t->next;
-               t->prev = t->next = NULL;
-       }
-       if (t == NULL)
-       {
-               if (taskqueue_state.queue_used > 0)
-               {
-                       t = taskqueue_state.queue_tasks[0];
-                       taskqueue_state.queue_used--;
-                       memmove(taskqueue_state.queue_tasks, taskqueue_state.queue_tasks + 1, taskqueue_state.queue_used * sizeof(taskqueue_task_t *));
-                       taskqueue_state.queue_tasks[taskqueue_state.queue_used] = NULL;
-               }
-       }
-       return t;
-}
-
-static void TaskQueue_ExecuteTask(taskqueue_task_t *t)
-{
-       // see if t is waiting on something
-       if (t->preceding && t->preceding->done == 0)
-               TaskQueue_Yield(t);
-       else
-               t->func(t);
-}
-
-// FIXME: don't use mutex
-// FIXME: this is basically fibers but less featureful - context switching for yield is not implemented
-static int TaskQueue_ThreadFunc(void *d)
-{
-       for (;;)
-       {
-               qboolean quit;
-               taskqueue_task_t *t = NULL;
-               Thread_AtomicLock(&taskqueue_state.command_lock);
-               quit = taskqueue_state.threads_quit != 0;
-               t = TaskQueue_GetPending();
-               Thread_AtomicUnlock(&taskqueue_state.command_lock);
-               if (t)
-                       TaskQueue_ExecuteTask(t);
-               else if (quit)
-                       break;
-       }
-       return 0;
-}
-
-void TaskQueue_Execute(qboolean force)
-{
-       // if we have no threads to run the tasks, just start executing them now
-       if (taskqueue_state.numthreads == 0 || force)
-       {
-               for (;;)
-               {
-                       taskqueue_task_t *t = NULL;
-                       Thread_AtomicLock(&taskqueue_state.command_lock);
-                       t = TaskQueue_GetPending();
-                       Thread_AtomicUnlock(&taskqueue_state.command_lock);
-                       if (!t)
-                               break;
-                       TaskQueue_ExecuteTask(t);
-               }
-       }
-}
-
-void TaskQueue_Enqueue(int numtasks, taskqueue_task_t *tasks)
-{
-       int i;
-       // try not to spinlock for a long time by breaking up large enqueues
-       while (numtasks > 64)
-       {
-               TaskQueue_Enqueue(64, tasks);
-               tasks += 64;
-               numtasks -= 64;
-       }
-       Thread_AtomicLock(&taskqueue_state.command_lock);
-       for (i = 0; i < numtasks; i++)
-       {
-               taskqueue_task_t *t = &tasks[i];
-               if (taskqueue_linkedlist.integer)
-               {
-                       // push to list.prev
-                       t->next = &taskqueue_state.list;
-                       t->prev = taskqueue_state.list.prev;
-                       t->next->prev = t;
-                       t->prev->next = t;
-               }
-               else
-               {
-                       if (taskqueue_state.queue_used >= taskqueue_state.queue_max)
-                       {
-                               taskqueue_state.queue_max *= 2;
-                               if (taskqueue_state.queue_max < 1024)
-                                       taskqueue_state.queue_max = 1024;
-                               taskqueue_state.queue_tasks = (taskqueue_task_t **)Mem_Realloc(cls.permanentmempool, taskqueue_state.queue_tasks, taskqueue_state.queue_max * sizeof(taskqueue_task_t *));
-                       }
-                       taskqueue_state.queue_tasks[taskqueue_state.queue_used++] = t;
-               }
-       }
-       Thread_AtomicUnlock(&taskqueue_state.command_lock);
-}
-
-// if the task can not be completed due yet to preconditions, just enqueue it again...
-void TaskQueue_Yield(taskqueue_task_t *t)
-{
-       t->yieldcount++;
-       TaskQueue_Enqueue(1, t);
-}
-
-void TaskQueue_WaitForTaskDone(taskqueue_task_t *t)
-{
-       qboolean done = false;
-       while (!done)
-       {
-               Thread_AtomicLock(&taskqueue_state.command_lock);
-               done = t->done != 0;
-               Thread_AtomicUnlock(&taskqueue_state.command_lock);
-               // if there are no threads, just execute the tasks immediately
-               if (!done && taskqueue_state.numthreads == 0)
-                       TaskQueue_Execute(true);
-       }
-}
-
-void TaskQueue_Frame(qboolean shutdown)
-{
-       int numthreads = shutdown ? 0 : bound(0, taskqueue_maxthreads.integer, sizeof(taskqueue_state.threads)/sizeof(taskqueue_state.threads[0]));
-#ifdef THREADDISABLE
-       numthreads = 0;
-#endif
-       if (taskqueue_state.numthreads != numthreads)
-       {
-               int i;
-               Thread_AtomicLock(&taskqueue_state.command_lock);
-               taskqueue_state.threads_quit = 1;
-               Thread_AtomicUnlock(&taskqueue_state.command_lock);
-               for (i = 0; i < taskqueue_state.numthreads; i++)
-               {
-                       if (taskqueue_state.threads[i].handle)
-                               Thread_WaitThread(taskqueue_state.threads[i].handle, 0);
-                       taskqueue_state.threads[i].handle = NULL;
-               }
-               Thread_AtomicLock(&taskqueue_state.command_lock);
-               taskqueue_state.threads_quit = 0;
-               Thread_AtomicUnlock(&taskqueue_state.command_lock);
-               taskqueue_state.numthreads = numthreads;
-               for (i = 0; i < taskqueue_state.numthreads; i++)
-                       taskqueue_state.threads[i].handle = Thread_CreateThread(TaskQueue_ThreadFunc, &taskqueue_state.threads[i]);
-               // if there are still pending tasks (e.g. no threads), execute them on main thread now
-               TaskQueue_Execute(true);
-       }
-}
-
-void TaskQueue_Setup(taskqueue_task_t *t, taskqueue_task_t *preceding, void(*func)(taskqueue_task_t *), size_t i0, size_t i1, void *p0, void *p1)
-{
-       memset(t, 0, sizeof(*t));
-       t->preceding = preceding;
-       t->func = func;
-       t->i[0] = i0;
-       t->i[1] = i1;
-       t->p[0] = p0;
-       t->p[1] = p1;
-}
-
-void TaskQueue_Task_CheckTasksDone(taskqueue_task_t *t)
-{
-       size_t numtasks = t->i[0];
-       taskqueue_task_t *tasks = t->p[0];
-       while (numtasks > 0)
-       {
-               // check the last task first as it's usually going to be the last to finish, so we do the least work by checking it first
-               if (!tasks[numtasks - 1].done)
-               {
-                       // update our partial progress, then yield to another pending task.
-                       t->i[0] = numtasks;
-                       TaskQueue_Yield(t);
-                       return;
-               }
-               numtasks--;
-       }
-       t->started = 1;
-       t->done = 1;
-}