diff --git a/include/proto/task.h b/include/proto/task.h index 424e8ba66..4044bd459 100644 --- a/include/proto/task.h +++ b/include/proto/task.h @@ -541,6 +541,19 @@ static inline int thread_has_tasks(void) !LIST_ISEMPTY(&task_per_thread[tid].task_list)); } +/* adds list item to work list and wake up the associated task */ +static inline void work_list_add(struct work_list *work, struct list *item) +{ + LIST_ADDQ_LOCKED(&work->head, item); + task_wakeup(work->task, TASK_WOKEN_OTHER); +} + +struct work_list *work_list_create(int nbthread, + struct task *(*fct)(struct task *, void *, unsigned short), + void *arg); + +void work_list_destroy(struct work_list *work, int nbthread); + /* * This does 3 things : * - wake up all expired tasks diff --git a/include/types/task.h b/include/types/task.h index ab909d0f8..a94905893 100644 --- a/include/types/task.h +++ b/include/types/task.h @@ -106,6 +106,23 @@ struct tasklet { * expire timer. The scheduler will requeue the task at the proper location. */ + +/* A work_list is a thread-safe way to enqueue some work to be run on another + * thread. It consists of a list, a task and a general-purpose argument. + * A work is appended to the list by atomically adding a list element to the + * list and waking up the associated task, which is done using work_add(). The + * caller must be careful about how operations are run as it will definitely + * happen that the element being enqueued is processed by the other thread + * before the call returns. Some locking conventions between the caller and the + * callee might sometimes be necessary. The task is always woken up with reason + * TASK_WOKEN_OTHER and a context pointing to the work_list entry. + */ +struct work_list { + struct list head; + struct task *task; + void *arg; +}; + #endif /* _TYPES_TASK_H */ /* diff --git a/src/task.c b/src/task.c index 2176a9190..9a55a604f 100644 --- a/src/task.c +++ b/src/task.c @@ -447,6 +447,51 @@ void process_runnable_tasks() activity[tid].long_rq++; } +/* create a work list array for threads, using tasks made of + * function . The context passed to the function will be the pointer to + * the thread's work list, which will contain a copy of argument . The + * wake up reason will be TASK_WOKEN_OTHER. The pointer to the work_list array + * is returned on success, otherwise NULL on failure. + */ +struct work_list *work_list_create(int nbthread, + struct task *(*fct)(struct task *, void *, unsigned short), + void *arg) +{ + struct work_list *wl; + int i; + + wl = calloc(nbthread, sizeof(*wl)); + if (!wl) + goto fail; + + for (i = 0; i < nbthread; i++) { + LIST_INIT(&wl[i].head); + wl[i].task = task_new(1UL << i); + if (!wl[i].task) + goto fail; + wl[i].task->process = fct; + wl[i].task->context = &wl[i]; + wl[i].arg = arg; + } + return wl; + + fail: + work_list_destroy(wl, nbthread); + return NULL; +} + +/* destroy work list */ +void work_list_destroy(struct work_list *work, int nbthread) +{ + int t; + + if (!work) + return; + for (t = 0; t < nbthread; t++) + task_destroy(work[t].task); + free(work); +} + /* * Delete every tasks before running the master polling loop */