diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
commit | 4897093455a2bf08f3db3a1132cc2f6f5484d77c (patch) | |
tree | 9e6373544263f003139431fb4b08f9766e1ed530 /support/misc/workqueue.c | |
parent | Initial commit. (diff) | |
download | nfs-utils-4897093455a2bf08f3db3a1132cc2f6f5484d77c.tar.xz nfs-utils-4897093455a2bf08f3db3a1132cc2f6f5484d77c.zip |
Adding upstream version 1:2.6.4.upstream/1%2.6.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'support/misc/workqueue.c')
-rw-r--r-- | support/misc/workqueue.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/support/misc/workqueue.c b/support/misc/workqueue.c new file mode 100644 index 0000000..8043268 --- /dev/null +++ b/support/misc/workqueue.c @@ -0,0 +1,228 @@ +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "workqueue.h" +#include "xlog.h" + +#if defined(HAVE_SCHED_H) && defined(HAVE_LIBPTHREAD) && defined(HAVE_UNSHARE) +#include <sched.h> +#include <pthread.h> + +struct xwork_struct { + struct xwork_struct *next; + void (*fn)(void *); + void *data; +}; + +struct xwork_queue { + struct xwork_struct *head; + struct xwork_struct **tail; + + unsigned char shutdown : 1; +}; + +static void xwork_queue_init(struct xwork_queue *queue) +{ + queue->head = NULL; + queue->tail = &queue->head; + queue->shutdown = 0; +} + +static void xwork_enqueue(struct xwork_queue *queue, + struct xwork_struct *entry) +{ + entry->next = NULL; + *queue->tail = entry; + queue->tail = &entry->next; +} + +static struct xwork_struct *xwork_dequeue(struct xwork_queue *queue) +{ + struct xwork_struct *entry = NULL; + if (queue->head) { + entry = queue->head; + queue->head = entry->next; + if (!queue->head) + queue->tail = &queue->head; + } + return entry; +} + +struct xthread_work { + struct xwork_struct work; + + pthread_cond_t cond; +}; + +struct xthread_workqueue { + struct xwork_queue queue; + + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +static void xthread_workqueue_init(struct xthread_workqueue *wq) +{ + xwork_queue_init(&wq->queue); + pthread_mutex_init(&wq->mutex, NULL); + pthread_cond_init(&wq->cond, NULL); +} + +static void xthread_workqueue_fini(struct xthread_workqueue *wq) +{ + pthread_cond_destroy(&wq->cond); + pthread_mutex_destroy(&wq->mutex); +} + +static int xthread_work_enqueue(struct xthread_workqueue *wq, + struct xthread_work *work) +{ + xwork_enqueue(&wq->queue, &work->work); + pthread_cond_signal(&wq->cond); + return 0; +} + +static struct xthread_work *xthread_work_dequeue(struct xthread_workqueue *wq) +{ + return (struct xthread_work *)xwork_dequeue(&wq->queue); +} + +static void xthread_workqueue_do_work(struct xthread_workqueue *wq) +{ + struct xthread_work *work; + + pthread_mutex_lock(&wq->mutex); + /* Signal the caller that we're up and running */ + pthread_cond_signal(&wq->cond); + for (;;) { + work = xthread_work_dequeue(wq); + if (work) { + work->work.fn(work->work.data); + pthread_cond_signal(&work->cond); + continue; + } + if (wq->queue.shutdown) + break; + pthread_cond_wait(&wq->cond, &wq->mutex); + } + pthread_mutex_unlock(&wq->mutex); +} + +void xthread_workqueue_shutdown(struct xthread_workqueue *wq) +{ + pthread_mutex_lock(&wq->mutex); + wq->queue.shutdown = 1; + pthread_cond_signal(&wq->cond); + pthread_mutex_unlock(&wq->mutex); +} + +static void xthread_workqueue_free(struct xthread_workqueue *wq) +{ + xthread_workqueue_fini(wq); + free(wq); +} + +static void xthread_workqueue_cleanup(void *data) +{ + xthread_workqueue_free(data); +} + +static void *xthread_workqueue_worker(void *data) +{ + pthread_cleanup_push(xthread_workqueue_cleanup, data); + xthread_workqueue_do_work(data); + pthread_cleanup_pop(1); + return NULL; +} + +struct xthread_workqueue *xthread_workqueue_alloc(void) +{ + struct xthread_workqueue *ret; + pthread_t thread; + + ret = malloc(sizeof(*ret)); + if (ret) { + xthread_workqueue_init(ret); + + pthread_mutex_lock(&ret->mutex); + if (pthread_create(&thread, NULL, + xthread_workqueue_worker, + ret) == 0) { + /* Wait for thread to start */ + pthread_cond_wait(&ret->cond, &ret->mutex); + pthread_mutex_unlock(&ret->mutex); + return ret; + } + pthread_mutex_unlock(&ret->mutex); + xthread_workqueue_free(ret); + ret = NULL; + } + return NULL; +} + +void xthread_work_run_sync(struct xthread_workqueue *wq, + void (*fn)(void *), void *data) +{ + struct xthread_work work = { + { + NULL, + fn, + data + }, + PTHREAD_COND_INITIALIZER, + }; + pthread_mutex_lock(&wq->mutex); + xthread_work_enqueue(wq, &work); + pthread_cond_wait(&work.cond, &wq->mutex); + pthread_mutex_unlock(&wq->mutex); + pthread_cond_destroy(&work.cond); +} + +static void xthread_workqueue_do_chroot(void *data) +{ + const char *path = data; + + if (unshare(CLONE_FS) != 0) { + xlog_err("unshare() failed: %m"); + return; + } + if (chroot(path) != 0) + xlog_err("chroot(%s) failed: %m", path); +} + +void xthread_workqueue_chroot(struct xthread_workqueue *wq, + const char *path) +{ + xthread_work_run_sync(wq, xthread_workqueue_do_chroot, (void *)path); +} + +#else + +struct xthread_workqueue { +}; + +static struct xthread_workqueue ret; + +struct xthread_workqueue *xthread_workqueue_alloc(void) +{ + return &ret; +} + +void xthread_workqueue_shutdown(struct xthread_workqueue *wq) +{ +} + +void xthread_work_run_sync(struct xthread_workqueue *wq, + void (*fn)(void *), void *data) +{ + fn(data); +} + +void xthread_workqueue_chroot(struct xthread_workqueue *wq, + const char *path) +{ + xlog_err("Unable to run as chroot"); +} + +#endif /* defined(HAVE_SCHED_H) && defined(HAVE_LIBPTHREAD) && defined(HAVE_UNSHARE) */ |