diff options
Diffstat (limited to 'pixman/pixman-worker-thread.c')
-rw-r--r-- | pixman/pixman-worker-thread.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/pixman/pixman-worker-thread.c b/pixman/pixman-worker-thread.c new file mode 100644 index 0000000..655850e --- /dev/null +++ b/pixman/pixman-worker-thread.c @@ -0,0 +1,293 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "pixman-private.h" + +#include <stdlib.h> +#include <pthread.h> +#include <sched.h> + +#define PIXMAN_NUM_WORKERS 4 +#define PIXMAN_QUEUE_SIZE 16 + +typedef struct pixman_composite_task pixman_composite_task_t; +typedef struct pixman_composite_task_queue pixman_composite_task_queue_t; +typedef struct pixman_worker pixman_worker_t; + +struct pixman_composite_task +{ + pixman_composite_func_t func; + pixman_implementation_t *imp; + pixman_composite_info_t info; +}; + +struct pixman_composite_task_queue +{ + pixman_composite_task_t tasks[PIXMAN_QUEUE_SIZE]; + int head; + int tail; +}; + +typedef enum +{ + PIXMAN_WORKER_VALID, + PIXMAN_WORKER_INVALID, +} pixman_worker_status_t; + +struct pixman_worker +{ + pixman_worker_status_t status; + pthread_t thread; + pixman_composite_task_queue_t queue; + pthread_mutex_t lock; + pthread_cond_t cond_ping; + pthread_cond_t cond_pong; +}; + +#ifdef PIXMAN_USE_WORKER_THREAD +/* Interface for task queue (ring buffer) */ +static inline pixman_bool_t +queue_is_empty (const pixman_composite_task_queue_t *queue) +{ + return queue->head == queue->tail; +} + +static inline pixman_bool_t +queue_is_full (const pixman_composite_task_queue_t *queue) +{ + int next_tail = queue->tail + 1; + + if (next_tail >= PIXMAN_QUEUE_SIZE) + return queue->head == 0; + + return next_tail == queue->head; +} + +static inline pixman_composite_task_t * +get_head (pixman_composite_task_queue_t *queue) +{ + if (!queue_is_empty (queue)) + return &(queue->tasks[queue->head]); + + return NULL; +} + +static inline pixman_bool_t +pop_head (pixman_composite_task_queue_t *queue) +{ + if (!queue_is_empty (queue)) + { + if (++(queue->head) == PIXMAN_QUEUE_SIZE) + queue->head = 0; + + return TRUE; + } + + return FALSE; +} + +static inline pixman_bool_t +push_tail (pixman_composite_task_queue_t *queue, pixman_composite_task_t *task) +{ + if (!queue_is_full (queue)) + { + queue->tasks[queue->tail] = *task; + if (++(queue->tail) == PIXMAN_QUEUE_SIZE) + queue->tail = 0; + + return TRUE; + } + + return FALSE; +} + +static pixman_bool_t use_worker = FALSE; +static pixman_worker_t workers[PIXMAN_NUM_WORKERS]; + +void *_pixman_worker_mainloop(void *arg) +{ + pixman_worker_t *worker = (pixman_worker_t*)arg; + pixman_composite_task_t *task; + + while (1) + { + pthread_mutex_lock (&(worker->lock)); + + if (queue_is_empty (&(worker->queue))) + { + pthread_cond_wait (&(worker->cond_ping), &(worker->lock)); + + if (queue_is_empty (&(worker->queue))) + { + pthread_mutex_unlock (&(worker->lock)); + pthread_exit (0); + } + } + + while (!queue_is_empty (&(worker->queue))) + { + task = get_head (&(worker->queue)); + task->func (task->imp, &(task->info)); + pop_head (&(worker->queue)); + } + + pthread_mutex_unlock (&(worker->lock)); + } +} + +void +_pixman_workers_fini (void); + +pixman_bool_t +_pixman_workers_init (void) +{ + int i; + + memset (workers, 0x00, PIXMAN_NUM_WORKERS * (int)sizeof (pixman_worker_t)); + + for (i = 0; i < PIXMAN_NUM_WORKERS; i++) + workers[i].status = PIXMAN_WORKER_INVALID; + + for (i = 0; i < PIXMAN_NUM_WORKERS; i++) + { + if (pthread_mutex_init (&workers[i].lock, NULL) != 0) + { + _pixman_log_error (FUNC, "Failed to initialize mutex\n"); + goto error; + } + + if (pthread_cond_init (&(workers[i].cond_ping), NULL)) + { + pthread_mutex_destroy (&(workers[i].lock)); + _pixman_log_error (FUNC, "Failed to initialize conditional variable\n"); + goto error; + } + + if (pthread_cond_init (&(workers[i].cond_pong), NULL)) + { + pthread_mutex_destroy (&(workers[i].lock)); + pthread_cond_destroy (&(workers[i].cond_ping)); + _pixman_log_error (FUNC, "Failed to initialize conditional variable\n"); + goto error; + } + + if (pthread_create (&(workers[i].thread), NULL, + _pixman_worker_mainloop, (void*)&workers[i]) != 0) + { + pthread_mutex_destroy (&(workers[i].lock)); + pthread_cond_destroy (&(workers[i].cond_ping)); + pthread_cond_destroy (&(workers[i].cond_pong)); + _pixman_log_error (FUNC, "Failed to create thread\n"); + goto error; + } + + workers[i].status = PIXMAN_WORKER_VALID; + } + + return TRUE; + +error: + _pixman_workers_fini (); + return FALSE; +} + +void +_pixman_workers_fini (void) +{ + int i; + + for (i = 0; i < PIXMAN_NUM_WORKERS; i++) + { + if (workers[i].status == PIXMAN_WORKER_VALID) + { + pthread_mutex_lock (&(workers[i].lock)); + + /* A signal without any new task will terminate the worker. */ + pthread_cond_signal (&(workers[i].cond_ping)); + + pthread_mutex_unlock (&(workers[i].lock)); + pthread_join (workers[i].thread, NULL); + pthread_mutex_destroy (&(workers[i].lock)); + pthread_cond_destroy (&(workers[i].cond_ping)); + pthread_cond_destroy (&(workers[i].cond_pong)); + } + } +} + +#ifdef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR +static void __attribute__((constructor)) +pixman_workers_constructor (void) +{ + if (_pixman_workers_init ()) + use_worker = TRUE; +} + +static void __attribute__((destructor)) +pixman_workers_destructor (void) +{ + _pixman_workers_fini (); +} +#endif + +void +_pixman_enqueue_composite (pixman_composite_func_t func, + pixman_implementation_t *imp, + pixman_composite_info_t *info, + pixman_bool_t wait_finish) +{ + if (use_worker && info->height > 32 && info->width > 32) + { + int i = 0; + int height; + pixman_composite_task_t task; + + task.func = func; + task.imp = imp; + task.info = *info; + + height = info->height / PIXMAN_NUM_WORKERS; + + for (i = 0; i < PIXMAN_NUM_WORKERS; i++) + { + task.info.src_y = info->src_y + height * i; + task.info.mask_y = info->mask_y + height * i; + task.info.dest_y = info->dest_y + height * i; + + if (i == (PIXMAN_NUM_WORKERS - 1)) + task.info.height = info->height - (PIXMAN_NUM_WORKERS - 1) * height; + else + task.info.height = height; + + pthread_mutex_lock (&(workers[i].lock)); + push_tail(&(workers[i].queue), &task); + pthread_cond_signal (&(workers[i].cond_ping)); + pthread_mutex_unlock (&(workers[i].lock)); + } + + + if (wait_finish) + { + sched_yield(); + + for (i = 0; i < PIXMAN_NUM_WORKERS; i++) + { + pthread_mutex_lock (&(workers[i].lock)); + + while (!queue_is_empty (&(workers[i].queue))) + { + pthread_mutex_unlock (&(workers[i].lock)); + sched_yield(); + pthread_mutex_lock (&(workers[i].lock)); + } + + pthread_mutex_unlock (&(workers[i].lock)); + } + } + } + else + { + func (imp, info); + } +} + +#endif //PIXMAN_USE_WORKER_THREAD |