From d44ccdd4378d6aafd1dd6322e419d1165635f25b Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 21 Jun 2012 12:51:12 +0200 Subject: libasys Signed-off-by: Jeremy Allison --- source3/lib/asys/asys.c | 274 +++++++++++++++++++++++++++++++++++++++++ source3/lib/asys/asys.h | 146 ++++++++++++++++++++++ source3/lib/asys/tests.c | 92 ++++++++++++++ source3/lib/asys/wscript_build | 9 ++ source3/wscript_build | 1 + 5 files changed, 522 insertions(+) create mode 100644 source3/lib/asys/asys.c create mode 100644 source3/lib/asys/asys.h create mode 100644 source3/lib/asys/tests.c create mode 100644 source3/lib/asys/wscript_build diff --git a/source3/lib/asys/asys.c b/source3/lib/asys/asys.c new file mode 100644 index 0000000000..766a716f9a --- /dev/null +++ b/source3/lib/asys/asys.c @@ -0,0 +1,274 @@ +/* + * Async syscalls + * Copyright (C) Volker Lendecke 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "asys.h" +#include +#include +#include "../pthreadpool/pthreadpool.h" + +struct asys_pwrite_args { + int fildes; + const void *buf; + size_t nbyte; + off_t offset; +}; + +struct asys_pread_args { + int fildes; + void *buf; + size_t nbyte; + off_t offset; +}; + +union asys_job_args { + struct asys_pwrite_args pwrite_args; + struct asys_pread_args pread_args; +}; + +struct asys_job { + void *private_data; + union asys_job_args args; + ssize_t ret; + int err; + char busy; + char canceled; +}; + +struct asys_context { + struct pthreadpool *pool; + int pthreadpool_fd; + + unsigned num_jobs; + struct asys_job **jobs; +}; + +struct asys_creds_context { + int dummy; +}; + +int asys_context_init(struct asys_context **pctx, unsigned max_parallel) +{ + struct asys_context *ctx; + int ret; + + ctx = calloc(1, sizeof(struct asys_context)); + if (ctx == NULL) { + return ENOMEM; + } + ret = pthreadpool_init(max_parallel, &ctx->pool); + if (ret != 0) { + free(ctx); + return ret; + } + ctx->pthreadpool_fd = pthreadpool_signal_fd(ctx->pool); + + *pctx = ctx; + return 0; +} + +int asys_signalfd(struct asys_context *ctx) +{ + return ctx->pthreadpool_fd; +} + +int asys_context_destroy(struct asys_context *ctx) +{ + int ret; + unsigned i; + + for (i=0; inum_jobs; i++) { + if (ctx->jobs[i]->busy) { + return EBUSY; + } + } + + ret = pthreadpool_destroy(ctx->pool); + if (ret != 0) { + return ret; + } + for (i=0; inum_jobs; i++) { + free(ctx->jobs[i]); + } + free(ctx->jobs); + free(ctx); + return 0; +} + +static int asys_new_job(struct asys_context *ctx, int *jobid, + struct asys_job **pjob) +{ + struct asys_job **tmp; + struct asys_job *job; + unsigned i; + + for (i=0; inum_jobs; i++) { + job = ctx->jobs[i]; + if (!job->busy) { + job->err = 0; + *pjob = job; + *jobid = i; + return 0; + } + } + + if (ctx->num_jobs+1 == 0) { + return EBUSY; /* overflow */ + } + + tmp = realloc(ctx->jobs, sizeof(struct asys_job *)*(ctx->num_jobs+1)); + if (tmp == NULL) { + return ENOMEM; + } + ctx->jobs = tmp; + + job = calloc(1, sizeof(struct asys_job)); + if (job == NULL) { + return ENOMEM; + } + ctx->jobs[ctx->num_jobs] = job; + + *jobid = ctx->num_jobs; + *pjob = job; + ctx->num_jobs += 1; + return 0; +} + +static void asys_pwrite_do(void *private_data); + +int asys_pwrite(struct asys_context *ctx, int fildes, const void *buf, + size_t nbyte, off_t offset, void *private_data) +{ + struct asys_job *job; + struct asys_pwrite_args *args; + int jobid; + int ret; + + ret = asys_new_job(ctx, &jobid, &job); + if (ret != 0) { + return ret; + } + job->private_data = private_data; + + args = &job->args.pwrite_args; + args->fildes = fildes; + args->buf = buf; + args->nbyte = nbyte; + args->offset = offset; + + ret = pthreadpool_add_job(ctx->pool, jobid, asys_pwrite_do, job); + if (ret != 0) { + return ret; + } + job->busy = 1; + + return 0; +} + +static void asys_pwrite_do(void *private_data) +{ + struct asys_job *job = (struct asys_job *)private_data; + struct asys_pwrite_args *args = &job->args.pwrite_args; + + job->ret = pwrite(args->fildes, args->buf, args->nbyte, args->offset); + if (job->ret == -1) { + job->err = errno; + } +} + +static void asys_pread_do(void *private_data); + +int asys_pread(struct asys_context *ctx, int fildes, void *buf, + size_t nbyte, off_t offset, void *private_data) +{ + struct asys_job *job; + struct asys_pread_args *args; + int jobid; + int ret; + + ret = asys_new_job(ctx, &jobid, &job); + if (ret != 0) { + return ret; + } + job->private_data = private_data; + + args = &job->args.pread_args; + args->fildes = fildes; + args->buf = buf; + args->nbyte = nbyte; + args->offset = offset; + + ret = pthreadpool_add_job(ctx->pool, jobid, asys_pread_do, job); + if (ret != 0) { + return ret; + } + job->busy = 1; + + return 0; +} + +static void asys_pread_do(void *private_data) +{ + struct asys_job *job = (struct asys_job *)private_data; + struct asys_pread_args *args = &job->args.pread_args; + + job->ret = pread(args->fildes, args->buf, args->nbyte, args->offset); + if (job->ret == -1) { + job->err = errno; + } +} + +void asys_cancel(struct asys_context *ctx, void *private_data) +{ + unsigned i; + + for (i=0; inum_jobs; i++) { + struct asys_job *job = ctx->jobs[i]; + + if (job->private_data == private_data) { + job->canceled = 1; + } + } +} + +int asys_result(struct asys_context *ctx, ssize_t *pret, int *perrno, + void *pdata) +{ + void **pprivate_data = (void **)pdata; + struct asys_job *job; + int ret, jobid; + + ret = pthreadpool_finished_job(ctx->pool, &jobid); + if (ret != 0) { + return ret; + } + if ((jobid < 0) || (jobid >= ctx->num_jobs)) { + return EIO; + } + + job = ctx->jobs[jobid]; + + if (job->canceled) { + return ECANCELED; + } + + *pret = job->ret; + *perrno = job->err; + *pprivate_data = job->private_data; + job->busy = 0; + return 0; +} diff --git a/source3/lib/asys/asys.h b/source3/lib/asys/asys.h new file mode 100644 index 0000000000..73f1051f61 --- /dev/null +++ b/source3/lib/asys/asys.h @@ -0,0 +1,146 @@ +/* + * Async syscalls + * Copyright (C) Volker Lendecke 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ASYS_H__ +#define __ASYS_H__ + +#include +#include +#include +#include + +/** + * @defgroup asys The async syscall library + * + * This module contains a set of asynchronous functions that directly + * wrap normally synchronous posix system calls. The reason for this + * module's existence is the limited set of operations the posix async + * I/O API provides. + * + * The basic flow of operations is: + * + * The application creates a asys_context structure using + * asys_context_create() + * + * The application triggers a call to the library by calling for + * example asys_ftruncate(). asys_ftruncate() takes a private_data + * argument that will be returned later by asys_result. The calling + * application should hand a pointer representing the async operation + * to the private_data argument. + * + * The application puts the fd returned by asys_signalfd() into its + * event loop. When the signal fd becomes readable, the application + * calls asys_result() to grab the final result of one of the system + * calls that were issued in the meantime. + * + * For multi-user applications it is necessary to create different + * credential contexts, as it is not clear when exactly the real + * system call will be issued. The application might have called + * seteuid(2) or something equivalent in the meantime. Thus, all + * system calls doing access checks, in particular all calls doing + * path-based operations, require a struct auth_creds_context + * parameter. asys_creds_context_create() creates such a context. All + * credential-checking operations take a struct asys_creds_context as + * an argument. It can be NULL if the application never changes + * credentials. + * + * @{ + */ + +struct asys_context; +struct asys_creds_context; + +enum asys_log_level { + ASYS_LOG_FATAL = 0, + ASYS_DEBUG_ERROR, + ASYS_DEBUG_WARNING, + ASYS_DEBUG_TRACE +}; + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +typedef void (*asys_log_fn)(struct asys_context *ctx, void *private_data, + enum asys_log_level level, + const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5); + +int asys_context_init(struct asys_context **ctx, unsigned max_parallel); +int asys_context_destroy(struct asys_context *ctx); +void asys_set_log_fn(struct asys_context *ctx, asys_log_fn fn, + void *private_data); + +/** + * @brief Get the the signal fd + * + * asys_signalfd() returns a file descriptor that will become readable + * whenever an asynchronous request has finished. When the signalfd is + * readable, calling asys_result() will not block. + * + * @param[in] ctx The asys context + * @return A file descriptor indicating a finished operation + */ + +int asys_signalfd(struct asys_context *ctx); + +/** + * @brief Pull the result from an async operation + * + * Whe the fd returned from asys_signalfd() is readable, an async + * operation has finished. The result from the async operation can be + * pulled with asys_result(). + * + * @param[in] ctx The asys context + * @return success: 0, failure: errno + */ +int asys_result(struct asys_context *ctx, ssize_t *pret, int *perrno, + void *pdata); +void asys_cancel(struct asys_context *ctx, void *private_data); + +int asys_pread(struct asys_context *ctx, int fildes, void *buf, size_t nbyte, + off_t offset, void *private_data); +int asys_pwrite(struct asys_context *ctx, int fildes, const void *buf, + size_t nbyte, off_t offset, void *private_data); +int asys_ftruncate(struct asys_context *ctx, int filedes, off_t length, + void *private_data); +int asys_fsync(struct asys_context *ctx, int fd, void *private_data); +int asys_close(struct asys_context *ctx, int fd, void *private_data); + +struct asys_creds_context *asys_creds_context_create( + struct asys_context *ctx, + uid_t uid, gid_t gid, unsigned num_gids, gid_t *gids); + +int asys_creds_context_delete(struct asys_creds_context *ctx); + +int asys_open(struct asys_context *ctx, struct asys_creds_context *cctx, + const char *pathname, int flags, mode_t mode, + void *private_data); +int asys_unlink(struct asys_context *ctx, struct asys_creds_context *cctx, + const char *pathname, void *private_data); + +/* @} */ + +#endif /* __ASYS_H__ */ diff --git a/source3/lib/asys/tests.c b/source3/lib/asys/tests.c new file mode 100644 index 0000000000..354f1bfbdb --- /dev/null +++ b/source3/lib/asys/tests.c @@ -0,0 +1,92 @@ +/* + * Test async syscalls + * Copyright (C) Volker Lendecke 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "asys.h" +#include +#include +#include +#include + +int main(int argc, const char *argv[]) +{ + struct asys_context *ctx; + int i, fd, ret; + + int *buf; + + int ntasks = 10; + + ret = asys_context_init(&ctx, 0); + if (ret != 0) { + perror("asys_context_create failed"); + return 1; + } + + fd = open("asys_testfile", O_CREAT|O_RDWR, 0644); + if (fd == -1) { + perror("open failed"); + return 1; + } + + buf = calloc(ntasks, sizeof(int)); + if (buf == NULL) { + perror("calloc failed"); + return 1; + } + + for (i=0; i