30#include "SDL_cpuinfo.h"
31#include "SDL_thread.h"
34#include "threadpool.h"
39#define THREADPOOL_TIMEOUT \
41#define THREADSIG_STOP ( 1 )
42#define THREADSIG_RUN \
48static int MAXTHREADS = 8;
82typedef struct ThreadQueueData_ {
83 int ( *function )(
void * );
90typedef struct ThreadData_ {
91 int ( *function )(
void * );
114static ThreadQueue *global_queue = NULL;
119static ThreadQueue *tq_create(
void );
120static void tq_enqueue( ThreadQueue *q,
void *data );
121static void *tq_dequeue( ThreadQueue *q );
122static void tq_destroy( ThreadQueue *q );
123static int threadpool_worker(
void *data );
124static int threadpool_handler(
void *data );
125static int vpool_worker(
void *data );
136static ThreadQueue *tq_create(
void )
142 q = calloc( 1,
sizeof( ThreadQueue ) );
145 n = calloc( 1,
sizeof(
Node ) );
151 q->t_lock = SDL_CreateMutex();
152 q->h_lock = SDL_CreateMutex();
153 q->r_lock = SDL_CreateMutex();
154 q->semaphore = SDL_CreateSemaphore( 0 );
165static void tq_enqueue( ThreadQueue *q,
void *data )
170 SDL_mutexP( q->r_lock );
171 if ( q->reserve != NULL ) {
173 q->reserve = n->
next;
175 n = malloc(
sizeof(
Node ) );
178 SDL_mutexV( q->r_lock );
181 SDL_mutexP( q->t_lock );
189 SDL_SemPost( q->semaphore );
190 SDL_mutexV( q->t_lock );
202static void *tq_dequeue( ThreadQueue *q )
208 SDL_mutexP( q->h_lock );
212 newhead =
node->next;
215 if ( newhead == NULL ) {
216 WARN( _(
"Tried to dequeue while the queue was empty!" ) );
225 newhead =
node->next;
226 }
while ( newhead == NULL );
234 SDL_mutexV( q->h_lock );
237 SDL_mutexP( q->r_lock );
238 node->next = q->reserve;
240 SDL_mutexV( q->r_lock );
252static void tq_destroy( ThreadQueue *q )
255 while ( q->first != NULL ) {
262 while ( q->reserve != NULL ) {
263 Node *n = q->reserve;
264 q->reserve = n->
next;
269 SDL_DestroySemaphore( q->semaphore );
270 SDL_DestroyMutex( q->h_lock );
271 SDL_DestroyMutex( q->t_lock );
272 SDL_DestroyMutex( q->r_lock );
275 if ( q->mutex != NULL )
276 SDL_DestroyMutex( q->mutex );
277 if ( q->cond != NULL )
278 SDL_DestroyCond( q->cond );
294static int threadpool_worker(
void *data )
301 while ( SDL_SemWait( work->semaphore ) == -1 ) {
304 WARN( _(
"SDL_SemWait failed! Error: %s" ), SDL_GetError() );
307 if ( work->signal == THREADSIG_STOP )
311 work->function( work->data );
314 tq_enqueue( work->idle, work );
317 tq_enqueue( work->stopped, work );
342static int threadpool_handler(
void *data )
345 int nrunning, newthread;
348 ThreadQueue *idle, *stopped;
353 stopped = tq_create();
356 threadargs = calloc( MAXTHREADS,
sizeof(
ThreadData ) );
359 for (
int i = 0; i < MAXTHREADS; i++ ) {
360 threadargs[i].function = NULL;
361 threadargs[i].data = NULL;
362 threadargs[i].semaphore =
363 SDL_CreateSemaphore( 0 );
364 threadargs[i].idle = idle;
365 threadargs[i].stopped = stopped;
366 threadargs[i].signal = THREADSIG_RUN;
368 tq_enqueue( stopped, &threadargs[i] );
383 if ( nrunning > 0 ) {
388 if ( SDL_SemWaitTimeout( global_queue->semaphore,
389 THREADPOOL_TIMEOUT ) != 0 ) {
391 if ( SDL_SemTryWait( idle->semaphore ) == 0 ) {
392 threadarg = tq_dequeue( idle );
394 threadarg->signal = THREADSIG_STOP;
396 SDL_SemPost( threadarg->semaphore );
410 if ( SDL_SemWait( global_queue->semaphore ) == -1 ) {
411 WARN( _(
"SDL_SemWait failed! Error: %s" ), SDL_GetError() );
422 node = tq_dequeue( global_queue );
430 if ( SDL_SemTryWait( idle->semaphore ) == 0 )
431 threadarg = tq_dequeue( idle );
433 else if ( SDL_SemTryWait( stopped->semaphore ) == 0 ) {
434 threadarg = tq_dequeue( stopped );
435 threadarg->signal = THREADSIG_RUN;
440 while ( SDL_SemWait( idle->semaphore ) == -1 ) {
442 WARN( _(
"SDL_SemWait failed! Error: %s" ), SDL_GetError() );
444 threadarg = tq_dequeue( idle );
448 threadarg->function =
node->function;
449 threadarg->data =
node->data;
451 SDL_SemPost( threadarg->semaphore );
455 SDL_CreateThread( threadpool_worker,
"threadpool_worker", threadarg );
463 tq_destroy( stopped );
474int threadpool_init(
void )
476 MAXTHREADS = SDL_GetCPUCount() + 1;
479 if ( global_queue != NULL ) {
480 WARN( _(
"Threadpool has already been initialized!" ) );
485 global_queue = tq_create();
488 if ( SDL_CreateThread( threadpool_handler,
"threadpool_handler", NULL ) ==
490 ERR( _(
"Threadpool init failed: %s" ), SDL_GetError() );
511ThreadQueue *vpool_create(
void )
513 ThreadQueue *tq = tq_create();
515 tq->cond = SDL_CreateCond();
516 tq->mutex = SDL_CreateMutex();
527void vpool_enqueue( ThreadQueue *queue,
int ( *function )(
void * ),
530 vpoolThreadData *arg = &
array_grow( &queue->arg );
531 memset( arg, 0,
sizeof( vpoolThreadData ) );
533 arg->cond = queue->cond;
534 arg->mutex = queue->mutex;
535 arg->count = &queue->cnt;
537 arg->node.data = data;
538 arg->node.function = function;
539 SDL_SemPost( queue->semaphore );
540 arg->wrapper.function = vpool_worker;
549static int vpool_worker(
void *data )
552 vpoolThreadData *work = (vpoolThreadData *)data;
555 work->node.function( work->node.data );
558 SDL_mutexP( work->mutex );
559 cnt = *( work->count ) - 1;
561 SDL_CondSignal( work->cond );
562 *( work->count ) = cnt;
563 SDL_mutexV( work->mutex );
573void vpool_wait( ThreadQueue *queue )
580 if ( global_queue == NULL ) {
581 WARN( _(
"Threadpool has not been initialized yet!" ) );
590 SDL_mutexP( queue->mutex );
592 for (
int i = 0; i < cnt; i++ ) {
593 vpoolThreadData *arg;
595 while ( SDL_SemWait( queue->semaphore ) == -1 ) {
597 WARN( _(
"SDL_SemWait failed! Error: %s" ), SDL_GetError() );
600 arg = &queue->arg[i];
601 arg->wrapper.data = arg;
602 tq_enqueue( global_queue, &queue->arg[i].wrapper );
606 SDL_CondWait( queue->cond, queue->mutex );
607 SDL_mutexV( queue->mutex );
617void vpool_cleanup( ThreadQueue *queue )
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Represents a node of an object. Each node can have multiple meshes and children nodes with an associa...
Data for the threadqueue.
Virtual thread pool data.