From: havoc Date: Wed, 29 Jan 2020 22:41:19 +0000 (+0000) Subject: Fix line endings on taskqueue.[ch] X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=86bcb3cfe5cf9df1a7e49fab8bb71e5bb995fdfb;p=xonotic%2Fdarkplaces.git Fix line endings on taskqueue.[ch] git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12517 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/taskqueue.c b/taskqueue.c index 35a6c753..b43f8d46 100644 --- a/taskqueue.c +++ b/taskqueue.c @@ -1,312 +1,312 @@ -#include "quakedef.h" -#include "taskqueue.h" - -cvar_t taskqueue_minthreads = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "taskqueue_minthreads", "4", "minimum number of threads to keep active for executing tasks"}; -cvar_t taskqueue_maxthreads = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "taskqueue_maxthreads", "32", "maximum number of threads to start up as needed based on task count"}; -cvar_t taskqueue_tasksperthread = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "taskqueue_tasksperthread", "4000", "expected amount of work that a single thread can do in a frame - the number of threads being used depends on the average workload in recent frames"}; - -#define MAXTHREADS 1024 -#define RECENTFRAMES 64 // averaging thread activity over this many frames to decide how many threads we need -#define THREADTASKS 256 // thread can hold this many tasks in its own queue -#define THREADBATCH 64 // thread will run this many tasks before checking status again -#define THREADSLEEPCOUNT 1000 // thread will sleep for a little while if it checks this many times and has no work to do - -typedef struct taskqueue_state_thread_s -{ - void *handle; - unsigned int quit; - unsigned int thread_index; - unsigned int tasks_completed; - - unsigned int enqueueposition; - unsigned int dequeueposition; - taskqueue_task_t *queue[THREADTASKS]; -} -taskqueue_state_thread_t; - -typedef struct taskqueue_state_s -{ - // TaskQueue_DistributeTasks cycles through the threads when assigning, each has its own queue - unsigned int enqueuethread; - int numthreads; - taskqueue_state_thread_t threads[MAXTHREADS]; - - // synchronization point for enqueue and some other memory access - Thread_SpinLock command_lock; - - // distributor queue (not assigned to threads yet, or waiting on other tasks) - unsigned int queue_enqueueposition; - unsigned int queue_dequeueposition; - unsigned int queue_size; - taskqueue_task_t **queue_data; - - // metrics to balance workload vs cpu resources - unsigned int tasks_recentframesindex; - unsigned int tasks_recentframes[RECENTFRAMES]; - unsigned int tasks_thisframe; - unsigned int tasks_averageperframe; -} -taskqueue_state_t; - -static taskqueue_state_t taskqueue_state; - -void TaskQueue_Init(void) -{ - Cvar_RegisterVariable(&taskqueue_minthreads); - Cvar_RegisterVariable(&taskqueue_maxthreads); - Cvar_RegisterVariable(&taskqueue_tasksperthread); -} - -void TaskQueue_Shutdown(void) -{ - if (taskqueue_state.numthreads) - TaskQueue_Frame(true); -} - -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) -{ - taskqueue_state_thread_t *s = (taskqueue_state_thread_t *)d; - unsigned int sleepcounter = 0; - for (;;) - { - qboolean quit; - while (s->dequeueposition != s->enqueueposition) - { - taskqueue_task_t *t = s->queue[s->dequeueposition % THREADTASKS]; - TaskQueue_ExecuteTask(t); - // when we advance, also clear the pointer for good measure - s->queue[s->dequeueposition++ % THREADTASKS] = NULL; - sleepcounter = 0; - } - Thread_AtomicLock(&taskqueue_state.command_lock); - quit = s->quit != 0; - Thread_AtomicUnlock(&taskqueue_state.command_lock); - if (quit) - break; - sleepcounter++; - if (sleepcounter >= THREADSLEEPCOUNT) - Sys_Sleep(1000); - sleepcounter = 0; - } - return 0; -} - -void TaskQueue_Enqueue(int numtasks, taskqueue_task_t *tasks) -{ - int i; - Thread_AtomicLock(&taskqueue_state.command_lock); - if (taskqueue_state.queue_size < - (taskqueue_state.queue_enqueueposition < taskqueue_state.queue_dequeueposition ? taskqueue_state.queue_size : 0) + - taskqueue_state.queue_enqueueposition - taskqueue_state.queue_dequeueposition + numtasks) - { - // we have to grow the queue... - unsigned int newsize = (taskqueue_state.queue_size + numtasks) * 2; - if (newsize < 1024) - newsize = 1024; - taskqueue_state.queue_data = Mem_Realloc(zonemempool, taskqueue_state.queue_data, sizeof(*taskqueue_state.queue_data) * newsize); - taskqueue_state.queue_size = newsize; - } - for (i = 0; i < numtasks; i++) - { - if (tasks[i].yieldcount == 0) - taskqueue_state.tasks_thisframe++; - taskqueue_state.queue_data[taskqueue_state.queue_enqueueposition] = &tasks[i]; - taskqueue_state.queue_enqueueposition++; - if (taskqueue_state.queue_enqueueposition >= taskqueue_state.queue_size) - taskqueue_state.queue_enqueueposition = 0; - } - 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); -} - -qboolean TaskQueue_IsDone(taskqueue_task_t *t) -{ - return !t->done != 0; -} - -void TaskQueue_DistributeTasks(void) -{ - Thread_AtomicLock(&taskqueue_state.command_lock); - if (taskqueue_state.numthreads > 0) - { - unsigned int attempts = taskqueue_state.numthreads; - while (attempts-- > 0 && taskqueue_state.queue_enqueueposition != taskqueue_state.queue_dequeueposition) - { - taskqueue_task_t *t = taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition]; - if (t->preceding && t->preceding->done == 0) - { - // task is waiting on something - // first dequeue it properly - taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition] = NULL; - taskqueue_state.queue_dequeueposition++; - if (taskqueue_state.queue_dequeueposition >= taskqueue_state.queue_size) - taskqueue_state.queue_dequeueposition = 0; - // now put it back in the distributor queue - we know there is room because we just made room - taskqueue_state.queue_data[taskqueue_state.queue_enqueueposition] = t; - taskqueue_state.queue_enqueueposition++; - if (taskqueue_state.queue_enqueueposition >= taskqueue_state.queue_size) - taskqueue_state.queue_enqueueposition = 0; - // we do not refresh the attempt counter here to avoid deadlock - quite often the only things sitting in the distributor queue are waiting on other tasks - } - else - { - taskqueue_state_thread_t *s = &taskqueue_state.threads[taskqueue_state.enqueuethread]; - if (s->enqueueposition - s->dequeueposition < THREADTASKS) - { - // add the task to the thread's queue - s->queue[(s->enqueueposition++) % THREADTASKS] = t; - // since we succeeded in assigning the task, advance the distributor queue - taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition] = NULL; - taskqueue_state.queue_dequeueposition++; - if (taskqueue_state.queue_dequeueposition >= taskqueue_state.queue_size) - taskqueue_state.queue_dequeueposition = 0; - // refresh our attempt counter because we did manage to assign something to a thread - attempts = taskqueue_state.numthreads; - } - } - } - } - Thread_AtomicUnlock(&taskqueue_state.command_lock); - // execute one pending task on the distributor queue, this matters if numthreads is 0 - if (taskqueue_state.queue_dequeueposition != taskqueue_state.queue_enqueueposition) - { - taskqueue_task_t *t = taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition]; - taskqueue_state.queue_dequeueposition++; - if (taskqueue_state.queue_dequeueposition >= taskqueue_state.queue_size) - taskqueue_state.queue_dequeueposition = 0; - if (t) - TaskQueue_ExecuteTask(t); - } -} - -void TaskQueue_WaitForTaskDone(taskqueue_task_t *t) -{ - qboolean done = false; - for (;;) - { - Thread_AtomicLock(&taskqueue_state.command_lock); - done = t->done != 0; - Thread_AtomicUnlock(&taskqueue_state.command_lock); - if (done) - break; - TaskQueue_DistributeTasks(); - } -} - -void TaskQueue_Frame(qboolean shutdown) -{ - int i; - unsigned long long int avg; - int maxthreads = bound(0, taskqueue_maxthreads.integer, MAXTHREADS); - int numthreads = maxthreads; - int tasksperthread = bound(10, taskqueue_tasksperthread.integer, 100000); -#ifdef THREADDISABLE - numthreads = 0; -#endif - - Thread_AtomicLock(&taskqueue_state.command_lock); - taskqueue_state.tasks_recentframesindex = (taskqueue_state.tasks_recentframesindex + 1) % RECENTFRAMES; - taskqueue_state.tasks_recentframes[taskqueue_state.tasks_recentframesindex] = taskqueue_state.tasks_thisframe; - taskqueue_state.tasks_thisframe = 0; - avg = 0; - for (i = 0; i < RECENTFRAMES; i++) - avg += taskqueue_state.tasks_recentframes[i]; - taskqueue_state.tasks_averageperframe = avg / RECENTFRAMES; - Thread_AtomicUnlock(&taskqueue_state.command_lock); - - numthreads = taskqueue_state.tasks_averageperframe / tasksperthread; - numthreads = bound(taskqueue_minthreads.integer, numthreads, taskqueue_maxthreads.integer); - - if (shutdown) - numthreads = 0; - - // check if we need to close some threads - if (taskqueue_state.numthreads > numthreads) - { - // tell extra threads to quit - Thread_AtomicLock(&taskqueue_state.command_lock); - for (i = numthreads; i < taskqueue_state.numthreads; i++) - taskqueue_state.threads[i].quit = 1; - Thread_AtomicUnlock(&taskqueue_state.command_lock); - for (i = numthreads; 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; - } - // okay we're at the new state now - taskqueue_state.numthreads = numthreads; - } - - // check if we need to start more threads - if (taskqueue_state.numthreads < numthreads) - { - // make sure we're not telling new threads to just quit on startup - Thread_AtomicLock(&taskqueue_state.command_lock); - for (i = taskqueue_state.numthreads; i < numthreads; i++) - taskqueue_state.threads[i].quit = 0; - Thread_AtomicUnlock(&taskqueue_state.command_lock); - - // start new threads - for (i = taskqueue_state.numthreads; i < numthreads; i++) - { - taskqueue_state.threads[i].thread_index = i; - taskqueue_state.threads[i].handle = Thread_CreateThread(TaskQueue_ThreadFunc, &taskqueue_state.threads[i]); - } - - // okay we're at the new state now - taskqueue_state.numthreads = numthreads; - } - - // just for good measure, distribute any pending tasks that span across frames - TaskQueue_DistributeTasks(); -} - -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; - // set our preceding task to one of the ones we are watching for - t->preceding = &tasks[numtasks - 1]; - TaskQueue_Yield(t); - return; - } - numtasks--; - } - t->done = 1; -} +#include "quakedef.h" +#include "taskqueue.h" + +cvar_t taskqueue_minthreads = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "taskqueue_minthreads", "4", "minimum number of threads to keep active for executing tasks"}; +cvar_t taskqueue_maxthreads = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "taskqueue_maxthreads", "32", "maximum number of threads to start up as needed based on task count"}; +cvar_t taskqueue_tasksperthread = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "taskqueue_tasksperthread", "4000", "expected amount of work that a single thread can do in a frame - the number of threads being used depends on the average workload in recent frames"}; + +#define MAXTHREADS 1024 +#define RECENTFRAMES 64 // averaging thread activity over this many frames to decide how many threads we need +#define THREADTASKS 256 // thread can hold this many tasks in its own queue +#define THREADBATCH 64 // thread will run this many tasks before checking status again +#define THREADSLEEPCOUNT 1000 // thread will sleep for a little while if it checks this many times and has no work to do + +typedef struct taskqueue_state_thread_s +{ + void *handle; + unsigned int quit; + unsigned int thread_index; + unsigned int tasks_completed; + + unsigned int enqueueposition; + unsigned int dequeueposition; + taskqueue_task_t *queue[THREADTASKS]; +} +taskqueue_state_thread_t; + +typedef struct taskqueue_state_s +{ + // TaskQueue_DistributeTasks cycles through the threads when assigning, each has its own queue + unsigned int enqueuethread; + int numthreads; + taskqueue_state_thread_t threads[MAXTHREADS]; + + // synchronization point for enqueue and some other memory access + Thread_SpinLock command_lock; + + // distributor queue (not assigned to threads yet, or waiting on other tasks) + unsigned int queue_enqueueposition; + unsigned int queue_dequeueposition; + unsigned int queue_size; + taskqueue_task_t **queue_data; + + // metrics to balance workload vs cpu resources + unsigned int tasks_recentframesindex; + unsigned int tasks_recentframes[RECENTFRAMES]; + unsigned int tasks_thisframe; + unsigned int tasks_averageperframe; +} +taskqueue_state_t; + +static taskqueue_state_t taskqueue_state; + +void TaskQueue_Init(void) +{ + Cvar_RegisterVariable(&taskqueue_minthreads); + Cvar_RegisterVariable(&taskqueue_maxthreads); + Cvar_RegisterVariable(&taskqueue_tasksperthread); +} + +void TaskQueue_Shutdown(void) +{ + if (taskqueue_state.numthreads) + TaskQueue_Frame(true); +} + +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) +{ + taskqueue_state_thread_t *s = (taskqueue_state_thread_t *)d; + unsigned int sleepcounter = 0; + for (;;) + { + qboolean quit; + while (s->dequeueposition != s->enqueueposition) + { + taskqueue_task_t *t = s->queue[s->dequeueposition % THREADTASKS]; + TaskQueue_ExecuteTask(t); + // when we advance, also clear the pointer for good measure + s->queue[s->dequeueposition++ % THREADTASKS] = NULL; + sleepcounter = 0; + } + Thread_AtomicLock(&taskqueue_state.command_lock); + quit = s->quit != 0; + Thread_AtomicUnlock(&taskqueue_state.command_lock); + if (quit) + break; + sleepcounter++; + if (sleepcounter >= THREADSLEEPCOUNT) + Sys_Sleep(1000); + sleepcounter = 0; + } + return 0; +} + +void TaskQueue_Enqueue(int numtasks, taskqueue_task_t *tasks) +{ + int i; + Thread_AtomicLock(&taskqueue_state.command_lock); + if (taskqueue_state.queue_size < + (taskqueue_state.queue_enqueueposition < taskqueue_state.queue_dequeueposition ? taskqueue_state.queue_size : 0) + + taskqueue_state.queue_enqueueposition - taskqueue_state.queue_dequeueposition + numtasks) + { + // we have to grow the queue... + unsigned int newsize = (taskqueue_state.queue_size + numtasks) * 2; + if (newsize < 1024) + newsize = 1024; + taskqueue_state.queue_data = Mem_Realloc(zonemempool, taskqueue_state.queue_data, sizeof(*taskqueue_state.queue_data) * newsize); + taskqueue_state.queue_size = newsize; + } + for (i = 0; i < numtasks; i++) + { + if (tasks[i].yieldcount == 0) + taskqueue_state.tasks_thisframe++; + taskqueue_state.queue_data[taskqueue_state.queue_enqueueposition] = &tasks[i]; + taskqueue_state.queue_enqueueposition++; + if (taskqueue_state.queue_enqueueposition >= taskqueue_state.queue_size) + taskqueue_state.queue_enqueueposition = 0; + } + 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); +} + +qboolean TaskQueue_IsDone(taskqueue_task_t *t) +{ + return !t->done != 0; +} + +void TaskQueue_DistributeTasks(void) +{ + Thread_AtomicLock(&taskqueue_state.command_lock); + if (taskqueue_state.numthreads > 0) + { + unsigned int attempts = taskqueue_state.numthreads; + while (attempts-- > 0 && taskqueue_state.queue_enqueueposition != taskqueue_state.queue_dequeueposition) + { + taskqueue_task_t *t = taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition]; + if (t->preceding && t->preceding->done == 0) + { + // task is waiting on something + // first dequeue it properly + taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition] = NULL; + taskqueue_state.queue_dequeueposition++; + if (taskqueue_state.queue_dequeueposition >= taskqueue_state.queue_size) + taskqueue_state.queue_dequeueposition = 0; + // now put it back in the distributor queue - we know there is room because we just made room + taskqueue_state.queue_data[taskqueue_state.queue_enqueueposition] = t; + taskqueue_state.queue_enqueueposition++; + if (taskqueue_state.queue_enqueueposition >= taskqueue_state.queue_size) + taskqueue_state.queue_enqueueposition = 0; + // we do not refresh the attempt counter here to avoid deadlock - quite often the only things sitting in the distributor queue are waiting on other tasks + } + else + { + taskqueue_state_thread_t *s = &taskqueue_state.threads[taskqueue_state.enqueuethread]; + if (s->enqueueposition - s->dequeueposition < THREADTASKS) + { + // add the task to the thread's queue + s->queue[(s->enqueueposition++) % THREADTASKS] = t; + // since we succeeded in assigning the task, advance the distributor queue + taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition] = NULL; + taskqueue_state.queue_dequeueposition++; + if (taskqueue_state.queue_dequeueposition >= taskqueue_state.queue_size) + taskqueue_state.queue_dequeueposition = 0; + // refresh our attempt counter because we did manage to assign something to a thread + attempts = taskqueue_state.numthreads; + } + } + } + } + Thread_AtomicUnlock(&taskqueue_state.command_lock); + // execute one pending task on the distributor queue, this matters if numthreads is 0 + if (taskqueue_state.queue_dequeueposition != taskqueue_state.queue_enqueueposition) + { + taskqueue_task_t *t = taskqueue_state.queue_data[taskqueue_state.queue_dequeueposition]; + taskqueue_state.queue_dequeueposition++; + if (taskqueue_state.queue_dequeueposition >= taskqueue_state.queue_size) + taskqueue_state.queue_dequeueposition = 0; + if (t) + TaskQueue_ExecuteTask(t); + } +} + +void TaskQueue_WaitForTaskDone(taskqueue_task_t *t) +{ + qboolean done = false; + for (;;) + { + Thread_AtomicLock(&taskqueue_state.command_lock); + done = t->done != 0; + Thread_AtomicUnlock(&taskqueue_state.command_lock); + if (done) + break; + TaskQueue_DistributeTasks(); + } +} + +void TaskQueue_Frame(qboolean shutdown) +{ + int i; + unsigned long long int avg; + int maxthreads = bound(0, taskqueue_maxthreads.integer, MAXTHREADS); + int numthreads = maxthreads; + int tasksperthread = bound(10, taskqueue_tasksperthread.integer, 100000); +#ifdef THREADDISABLE + numthreads = 0; +#endif + + Thread_AtomicLock(&taskqueue_state.command_lock); + taskqueue_state.tasks_recentframesindex = (taskqueue_state.tasks_recentframesindex + 1) % RECENTFRAMES; + taskqueue_state.tasks_recentframes[taskqueue_state.tasks_recentframesindex] = taskqueue_state.tasks_thisframe; + taskqueue_state.tasks_thisframe = 0; + avg = 0; + for (i = 0; i < RECENTFRAMES; i++) + avg += taskqueue_state.tasks_recentframes[i]; + taskqueue_state.tasks_averageperframe = avg / RECENTFRAMES; + Thread_AtomicUnlock(&taskqueue_state.command_lock); + + numthreads = taskqueue_state.tasks_averageperframe / tasksperthread; + numthreads = bound(taskqueue_minthreads.integer, numthreads, taskqueue_maxthreads.integer); + + if (shutdown) + numthreads = 0; + + // check if we need to close some threads + if (taskqueue_state.numthreads > numthreads) + { + // tell extra threads to quit + Thread_AtomicLock(&taskqueue_state.command_lock); + for (i = numthreads; i < taskqueue_state.numthreads; i++) + taskqueue_state.threads[i].quit = 1; + Thread_AtomicUnlock(&taskqueue_state.command_lock); + for (i = numthreads; 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; + } + // okay we're at the new state now + taskqueue_state.numthreads = numthreads; + } + + // check if we need to start more threads + if (taskqueue_state.numthreads < numthreads) + { + // make sure we're not telling new threads to just quit on startup + Thread_AtomicLock(&taskqueue_state.command_lock); + for (i = taskqueue_state.numthreads; i < numthreads; i++) + taskqueue_state.threads[i].quit = 0; + Thread_AtomicUnlock(&taskqueue_state.command_lock); + + // start new threads + for (i = taskqueue_state.numthreads; i < numthreads; i++) + { + taskqueue_state.threads[i].thread_index = i; + taskqueue_state.threads[i].handle = Thread_CreateThread(TaskQueue_ThreadFunc, &taskqueue_state.threads[i]); + } + + // okay we're at the new state now + taskqueue_state.numthreads = numthreads; + } + + // just for good measure, distribute any pending tasks that span across frames + TaskQueue_DistributeTasks(); +} + +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; + // set our preceding task to one of the ones we are watching for + t->preceding = &tasks[numtasks - 1]; + TaskQueue_Yield(t); + return; + } + numtasks--; + } + t->done = 1; +} diff --git a/taskqueue.h b/taskqueue.h index c7a53d98..9e8741f4 100644 --- a/taskqueue.h +++ b/taskqueue.h @@ -1,50 +1,50 @@ - -#ifndef TASKQUEUE_H -#define TASKQUEUE_H - -#include "qtypes.h" -#include "thread.h" - -typedef struct taskqueue_task_s -{ - // if not NULL, this task must be done before this one will dequeue (faster than simply calling TaskQueue_Yield immediately) - struct taskqueue_task_s *preceding; - - // use TaskQueue_IsDone() to poll done status - volatile int done; - - // function to call, and parameters for it to use - void(*func)(struct taskqueue_task_s *task); - // general purpose parameters - void *p[2]; - size_t i[2]; - - unsigned int yieldcount; // number of times this task has been requeued - each task counts only once for purposes of tasksperthread averaging -} -taskqueue_task_t; - -// queue the tasks to be executed, but does not start them (until TaskQueue_WaitforTaskDone is called) -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, does not cause tasks to be executed (see TaskQueue_WaitForTaskDone for that) -qboolean TaskQueue_IsDone(taskqueue_task_t *t); - -// triggers execution of queued tasks, and waits for the specified task to be done -void TaskQueue_WaitForTaskDone(taskqueue_task_t *t); - -// 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); - -void TaskQueue_Init(void); -void TaskQueue_Shutdown(void); -void TaskQueue_Frame(qboolean shutdown); - -#endif + +#ifndef TASKQUEUE_H +#define TASKQUEUE_H + +#include "qtypes.h" +#include "thread.h" + +typedef struct taskqueue_task_s +{ + // if not NULL, this task must be done before this one will dequeue (faster than simply calling TaskQueue_Yield immediately) + struct taskqueue_task_s *preceding; + + // use TaskQueue_IsDone() to poll done status + volatile int done; + + // function to call, and parameters for it to use + void(*func)(struct taskqueue_task_s *task); + // general purpose parameters + void *p[2]; + size_t i[2]; + + unsigned int yieldcount; // number of times this task has been requeued - each task counts only once for purposes of tasksperthread averaging +} +taskqueue_task_t; + +// queue the tasks to be executed, but does not start them (until TaskQueue_WaitforTaskDone is called) +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, does not cause tasks to be executed (see TaskQueue_WaitForTaskDone for that) +qboolean TaskQueue_IsDone(taskqueue_task_t *t); + +// triggers execution of queued tasks, and waits for the specified task to be done +void TaskQueue_WaitForTaskDone(taskqueue_task_t *t); + +// 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); + +void TaskQueue_Init(void); +void TaskQueue_Shutdown(void); +void TaskQueue_Frame(qboolean shutdown); + +#endif