commit 61b316d77f70af04e534a2a1340d1d16941fb97b parent afc16da0d6d3fc9562e6c6f858dc140fcc4b33ea Author: Michael Savage <mikejsavage@gmail.com> Date: Mon Aug 10 22:24:26 +0200 Add untested/preliminary background work queue implementation Diffstat:
Makefile | | | 2 | +- |
work_queue.cc | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
work_queue.h | | | 11 | +++++++++++ |
diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ all: medfall hm.so pp -OBJS = main.o gl.o +OBJS = main.o gl.o work_queue.o BSPOBJS = bsp.o bsp_renderer.o gl.o HMOBJS = hm.o heightmap.o terrain_manager.o stb_image.o stb_perlin.o PPOBJS = pp.o stb_image.o stb_image_write.o diff --git a/work_queue.cc b/work_queue.cc @@ -0,0 +1,95 @@ +#include <pthread.h> +#include <semaphore.h> + +#include "int.h" +#include "work_queue.h" + +// TODO: move me somewhere else +#define assert( predicate ) { if( !( predicate ) ) { __builtin_trap(); } } +#define array_len( x ) ( sizeof( x ) / sizeof( ( x )[ 0 ] ) ) + +#define read_barrier() asm volatile ( "" ::: "memory" ) +#define write_barrier() asm volatile ( "" ::: "memory" ) + +struct Job { + WorkQueueCallback * callback; + void * data; +}; + +struct WorkQueue { + Job jobs[ 256 ]; + + sem_t * sem; + + // using head/length means we need to an atomic pair which is a pain + volatile u16 head; + volatile u16 tail; + + volatile u16 jobs_queued; + volatile u16 jobs_completed; +}; + +static bool workqueue_step( WorkQueue * const queue ) { + const u16 current_head = queue->head; + const u16 new_head = ( current_head + 1 ) % array_len( queue->jobs ); + + if( queue->jobs_completed < queue->jobs_queued ) { + if( __sync_bool_compare_and_swap( &queue->head, current_head, new_head ) ) { + Job & job = queue->jobs[ current_head ]; + + job.callback( job.data ); + + __sync_fetch_and_add( &queue->jobs_completed, 1 ); + } + + return true; + } + + return false; +} + +static void * workqueue_worker( void * data ) { + WorkQueue * queue = ( WorkQueue * ) data; + + for( ;; ) { + if( !workqueue_step( queue ) ) { + sem_wait( queue->sem ); + } + } + + return nullptr; +} + +void workqueue_init( WorkQueue * const queue, const u32 num_threads ) { + *queue = { }; + + sem_init( queue->sem, 0, 0 ); + + for( u32 i = 0; i < num_threads; i++ ) { + pthread_t thread; + pthread_create( &thread, nullptr, workqueue_worker, queue ); + } +} + +void workqueue_enqueue( WorkQueue * const queue, WorkQueueCallback * const callback, void * const data ) { + assert( queue->jobs_queued < array_len( queue->jobs ) ); + + const Job job = { callback, data }; + + queue->jobs[ queue->tail ] = job; + queue->jobs_queued++; + + write_barrier(); + queue->tail = ( queue->tail + 1 ) % array_len( queue->jobs ); + + sem_post( queue->sem ); +} + +void workqueue_exhaust( WorkQueue * const queue ) { + while( queue->jobs_completed < queue->jobs_queued ) { + workqueue_step( queue ); + } + + queue->jobs_queued = 0; + queue->jobs_completed = 0; +} diff --git a/work_queue.h b/work_queue.h @@ -0,0 +1,11 @@ +#ifndef _WORK_QUEUE_H_ +#define _WORK_QUEUE_H_ + +#include "int.h" + +struct WorkQueue; + +#define WORK_QUEUE_CALLBACK( name ) void name( void * const data ) +typedef WORK_QUEUE_CALLBACK( WorkQueueCallback ); + +#endif // _WORK_QUEUE_H_