diff options
Diffstat (limited to 'lib/librt')
-rw-r--r-- | lib/librt/Makefile | 16 | ||||
-rw-r--r-- | lib/librt/Version.map | 69 | ||||
-rw-r--r-- | lib/librt/aio.c | 196 | ||||
-rw-r--r-- | lib/librt/mq.c | 232 | ||||
-rw-r--r-- | lib/librt/sigev_thread.c | 465 | ||||
-rw-r--r-- | lib/librt/sigev_thread.h | 85 | ||||
-rw-r--r-- | lib/librt/timer.c | 181 |
7 files changed, 1244 insertions, 0 deletions
diff --git a/lib/librt/Makefile b/lib/librt/Makefile new file mode 100644 index 0000000..00d8cda --- /dev/null +++ b/lib/librt/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +LIB=rt +SHLIB_MAJOR= 1 +CFLAGS+=-I${.CURDIR}/../libc/include -I${.CURDIR} +CFLAGS+=-Winline -Wall -g + +#MAN= libthr.3 + +SRCS+= aio.c mq.c sigev_thread.c timer.c + +PRECIOUSLIB= + +VERSION_MAP= ${.CURDIR}/Version.map + +.include <bsd.lib.mk> diff --git a/lib/librt/Version.map b/lib/librt/Version.map new file mode 100644 index 0000000..161bb76 --- /dev/null +++ b/lib/librt/Version.map @@ -0,0 +1,69 @@ +/* + * $FreeBSD$ + */ + +FBSD_1.0 { + aio_read; + aio_write; + aio_return; + aio_waitcomplete; + aio_fsync; + mq_open; + mq_close; + mq_notify; + mq_getattr; + mq_setattr; + mq_timedreceive; + mq_timedsend; + mq_unlink; + mq_send; + mq_receive; + timer_create; + timer_delete; + timer_gettime; + timer_settime; + timer_getoverrun; +}; + +FBSDprivate_1.0 { + _aio_read; + _aio_write; + _aio_return; + _aio_waitcomplete; + _aio_fsync; + __aio_read; + __aio_write; + __aio_return; + __aio_waitcomplete; + __aio_fsync; + _mq_open; + _mq_close; + _mq_notify; + _mq_getattr; + _mq_setattr; + _mq_timedreceive; + _mq_timedsend; + _mq_unlink; + _mq_send; + _mq_receive; + __mq_open; + __mq_close; + __mq_notify; + __mq_getattr; + __mq_setattr; + __mq_timedreceive; + __mq_timedsend; + __mq_unlink; + __mq_send; + __mq_receive; + _timer_create; + _timer_delete; + _timer_gettime; + _timer_settime; + _timer_getoverrun; + __timer_create; + __timer_delete; + __timer_gettime; + __timer_settime; + __timer_getoverrun; +}; diff --git a/lib/librt/aio.c b/lib/librt/aio.c new file mode 100644 index 0000000..25d7662 --- /dev/null +++ b/lib/librt/aio.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/aio.h> + +#include "namespace.h" +#include <errno.h> +#include <stddef.h> +#include <signal.h> +#include "sigev_thread.h" +#include "un-namespace.h" + +__weak_reference(__aio_read, _aio_read); +__weak_reference(__aio_read, aio_read); +__weak_reference(__aio_write, _aio_write); +__weak_reference(__aio_write, aio_write); +__weak_reference(__aio_return, _aio_return); +__weak_reference(__aio_return, aio_return); +__weak_reference(__aio_waitcomplete, _aio_waitcomplete); +__weak_reference(__aio_waitcomplete, aio_waitcomplete); +__weak_reference(__aio_fsync, _aio_fsync); +__weak_reference(__aio_fsync, aio_fsync); + +typedef void (*aio_func)(union sigval val, struct aiocb *iocb); + +extern int __sys_aio_read(struct aiocb *iocb); +extern int __sys_aio_write(struct aiocb *iocb); +extern int __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout); +extern int __sys_aio_return(struct aiocb *iocb); +extern int __sys_aio_error(struct aiocb *iocb); +extern int __sys_aio_fsync(int op, struct aiocb *iocb); + +static void +aio_dispatch(struct sigev_node *sn) +{ + aio_func f = sn->sn_func; + + f(sn->sn_value, (struct aiocb *)sn->sn_id); +} + +static int +aio_sigev_alloc(struct aiocb *iocb, struct sigev_node **sn, + struct sigevent *saved_ev) +{ + if (__sigev_check_init()) { + /* This might be that thread library is not enabled. */ + errno = EINVAL; + return (-1); + } + + *sn = __sigev_alloc(SI_ASYNCIO, &iocb->aio_sigevent, NULL, 1); + if (*sn == NULL) { + errno = EAGAIN; + return (-1); + } + + *saved_ev = iocb->aio_sigevent; + (*sn)->sn_id = (sigev_id_t)iocb; + __sigev_get_sigevent(*sn, &iocb->aio_sigevent, (*sn)->sn_id); + (*sn)->sn_dispatch = aio_dispatch; + + __sigev_list_lock(); + __sigev_register(*sn); + __sigev_list_unlock(); + + return (0); +} + +static int +aio_io(struct aiocb *iocb, int (*sysfunc)(struct aiocb *iocb)) +{ + struct sigev_node *sn; + struct sigevent saved_ev; + int ret, err; + + if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) { + ret = sysfunc(iocb); + return (ret); + } + + ret = aio_sigev_alloc(iocb, &sn, &saved_ev); + if (ret) + return (ret); + ret = sysfunc(iocb); + iocb->aio_sigevent = saved_ev; + if (ret != 0) { + err = errno; + __sigev_list_lock(); + __sigev_delete_node(sn); + __sigev_list_unlock(); + errno = err; + } + return (ret); +} + +int +__aio_read(struct aiocb *iocb) +{ + + return aio_io(iocb, &__sys_aio_read); +} + +int +__aio_write(struct aiocb *iocb) +{ + + return aio_io(iocb, &__sys_aio_write); +} + +int +__aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout) +{ + int err; + int ret = __sys_aio_waitcomplete(iocbp, timeout); + + if (*iocbp) { + if ((*iocbp)->aio_sigevent.sigev_notify == SIGEV_THREAD) { + err = errno; + __sigev_list_lock(); + __sigev_delete(SI_ASYNCIO, (sigev_id_t)(*iocbp)); + __sigev_list_unlock(); + errno = err; + } + } + + return (ret); +} + +int +__aio_return(struct aiocb *iocb) +{ + + if (iocb->aio_sigevent.sigev_notify == SIGEV_THREAD) { + if (__sys_aio_error(iocb) == EINPROGRESS) + return (EINPROGRESS); + __sigev_list_lock(); + __sigev_delete(SI_ASYNCIO, (sigev_id_t)iocb); + __sigev_list_unlock(); + } + + return __sys_aio_return(iocb); +} + +int +__aio_fsync(int op, struct aiocb *iocb) +{ + struct sigev_node *sn; + struct sigevent saved_ev; + int ret, err; + + if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) + return __sys_aio_fsync(op, iocb); + + ret = aio_sigev_alloc(iocb, &sn, &saved_ev); + if (ret) + return (ret); + ret = __sys_aio_fsync(op, iocb); + iocb->aio_sigevent = saved_ev; + if (ret != 0) { + err = errno; + __sigev_list_lock(); + __sigev_delete_node(sn); + __sigev_list_unlock(); + errno = err; + } + return (ret); +} diff --git a/lib/librt/mq.c b/lib/librt/mq.c new file mode 100644 index 0000000..9bdb503 --- /dev/null +++ b/lib/librt/mq.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 2006 David Xu <davidxu@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/mqueue.h> + +#include "namespace.h" +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include <signal.h> +#include "sigev_thread.h" +#include "un-namespace.h" + +extern int __sys_kmq_notify(int, const struct sigevent *); +extern int __sys_kmq_open(const char *, int, mode_t, + const struct mq_attr *); +extern int __sys_kmq_setattr(int, const struct mq_attr *__restrict, + struct mq_attr *__restrict); +extern ssize_t __sys_kmq_timedreceive(int, char *__restrict, size_t, + unsigned *__restrict, const struct timespec *__restrict); +extern int __sys_kmq_timedsend(int, const char *, size_t, unsigned, + const struct timespec *); +extern int __sys_kmq_unlink(const char *); +extern int __sys_close(int fd); + +struct __mq { + int oshandle; + struct sigev_node *node; +}; + +__weak_reference(__mq_open, mq_open); +__weak_reference(__mq_open, _mq_open); +__weak_reference(__mq_close, mq_close); +__weak_reference(__mq_close, _mq_close); +__weak_reference(__mq_notify, mq_notify); +__weak_reference(__mq_notify, _mq_notify); +__weak_reference(__mq_getattr, mq_getattr); +__weak_reference(__mq_getattr, _mq_getattr); +__weak_reference(__mq_setattr, mq_setattr); +__weak_reference(__mq_setattr, _mq_setattr); +__weak_reference(__mq_timedreceive, mq_timedreceive); +__weak_reference(__mq_timedreceive, _mq_timedreceive); +__weak_reference(__mq_timedsend, mq_timedsend); +__weak_reference(__mq_timedsend, _mq_timedsend); +__weak_reference(__mq_unlink, mq_unlink); +__weak_reference(__mq_unlink, _mq_unlink); +__weak_reference(__mq_send, mq_send); +__weak_reference(__mq_send, _mq_send); +__weak_reference(__mq_receive, mq_receive); +__weak_reference(__mq_receive, _mq_receive); + +mqd_t +__mq_open(const char *name, int oflag, mode_t mode, + const struct mq_attr *attr) +{ + struct __mq *mq; + int err; + + mq = malloc(sizeof(struct __mq)); + if (mq == NULL) + return (NULL); + + mq->oshandle = __sys_kmq_open(name, oflag, mode, attr); + if (mq->oshandle != -1) { + mq->node = NULL; + return (mq); + } + err = errno; + free(mq); + errno = err; + return ((mqd_t)-1L); +} + +int +__mq_close(mqd_t mqd) +{ + int h; + + if (mqd->node != NULL) { + __sigev_list_lock(); + __sigev_delete_node(mqd->node); + __sigev_list_unlock(); + } + h = mqd->oshandle; + free(mqd); + return (__sys_close(h)); +} + +typedef void (*mq_func)(union sigval val); + +static void +mq_dispatch(struct sigev_node *sn) +{ + mq_func f = sn->sn_func; + + /* + * Check generation before calling user function, + * this should avoid expired notification. + */ + if (sn->sn_gen == sn->sn_info.si_value.sival_int) + f(sn->sn_value); +} + +int +__mq_notify(mqd_t mqd, const struct sigevent *evp) +{ + struct sigevent ev; + struct sigev_node *sn; + int ret; + + if (evp == NULL || evp->sigev_notify != SIGEV_THREAD) { + if (mqd->node != NULL) { + __sigev_list_lock(); + __sigev_delete_node(mqd->node); + mqd->node = NULL; + __sigev_list_unlock(); + } + return __sys_kmq_notify(mqd->oshandle, evp); + } + + if (__sigev_check_init()) { + /* + * Thread library is not enabled. + */ + errno = EINVAL; + return (-1); + } + + sn = __sigev_alloc(SI_MESGQ, evp, mqd->node, 1); + if (sn == NULL) { + errno = EAGAIN; + return (-1); + } + + sn->sn_id = mqd->oshandle; + sn->sn_dispatch = mq_dispatch; + __sigev_get_sigevent(sn, &ev, sn->sn_gen); + __sigev_list_lock(); + if (mqd->node != NULL) + __sigev_delete_node(mqd->node); + mqd->node = sn; + __sigev_register(sn); + ret = __sys_kmq_notify(mqd->oshandle, &ev); + __sigev_list_unlock(); + return (ret); +} + +int +__mq_getattr(mqd_t mqd, struct mq_attr *attr) +{ + + return __sys_kmq_setattr(mqd->oshandle, NULL, attr); +} + +int +__mq_setattr(mqd_t mqd, const struct mq_attr *newattr, struct mq_attr *oldattr) +{ + + return __sys_kmq_setattr(mqd->oshandle, newattr, oldattr); +} + +ssize_t +__mq_timedreceive(mqd_t mqd, char *buf, size_t len, + unsigned *prio, const struct timespec *timeout) +{ + + return __sys_kmq_timedreceive(mqd->oshandle, buf, len, prio, timeout); +} + +ssize_t +__mq_receive(mqd_t mqd, char *buf, size_t len, unsigned *prio) +{ + + return __sys_kmq_timedreceive(mqd->oshandle, buf, len, prio, NULL); +} + +ssize_t +__mq_timedsend(mqd_t mqd, char *buf, size_t len, + unsigned prio, const struct timespec *timeout) +{ + + return __sys_kmq_timedsend(mqd->oshandle, buf, len, prio, timeout); +} + +ssize_t +__mq_send(mqd_t mqd, char *buf, size_t len, unsigned prio) +{ + + return __sys_kmq_timedsend(mqd->oshandle, buf, len, prio, NULL); +} + +int +__mq_unlink(const char *path) +{ + + return __sys_kmq_unlink(path); +} + +int +__mq_oshandle(mqd_t mqd) +{ + + return (mqd->oshandle); +} diff --git a/lib/librt/sigev_thread.c b/lib/librt/sigev_thread.c new file mode 100644 index 0000000..928ba1e --- /dev/null +++ b/lib/librt/sigev_thread.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#include <sys/types.h> +#include <machine/atomic.h> + +#include "namespace.h" +#include <err.h> +#include <errno.h> +#include <ucontext.h> +#include <sys/thr.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "sigev_thread.h" + +LIST_HEAD(sigev_list_head, sigev_node); +#define HASH_QUEUES 17 +#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) + +static struct sigev_list_head sigev_hash[HASH_QUEUES]; +static struct sigev_list_head sigev_all; +static LIST_HEAD(,sigev_thread) sigev_threads; +static int sigev_generation; +static pthread_mutex_t *sigev_list_mtx; +static pthread_once_t sigev_once = PTHREAD_ONCE_INIT; +static pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; +static struct sigev_thread *sigev_default_thread; +static pthread_attr_t sigev_default_attr; +static int atfork_registered; + +static void __sigev_fork_prepare(void); +static void __sigev_fork_parent(void); +static void __sigev_fork_child(void); +static struct sigev_thread *sigev_thread_create(int); +static void *sigev_service_loop(void *); +static void *worker_routine(void *); +static void worker_cleanup(void *); + +#pragma weak _pthread_create + +static void +attrcopy(pthread_attr_t *src, pthread_attr_t *dst) +{ + struct sched_param sched; + void *a; + size_t u; + int v; + + _pthread_attr_getschedpolicy(src, &v); + _pthread_attr_setschedpolicy(dst, v); + + _pthread_attr_getinheritsched(src, &v); + _pthread_attr_setinheritsched(dst, v); + + _pthread_attr_getschedparam(src, &sched); + _pthread_attr_setschedparam(dst, &sched); + + _pthread_attr_getscope(src, &v); + _pthread_attr_setscope(dst, v); + + _pthread_attr_getstacksize(src, &u); + _pthread_attr_setstacksize(dst, u); + + _pthread_attr_getstackaddr(src, &a); + _pthread_attr_setstackaddr(src, a); + + _pthread_attr_getguardsize(src, &u); + _pthread_attr_setguardsize(dst, u); +} + +static __inline int +have_threads(void) +{ + return (&_pthread_create != NULL); +} + +void +__sigev_thread_init(void) +{ + static int inited = 0; + pthread_mutexattr_t mattr; + int i; + + _pthread_mutexattr_init(&mattr); + _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); + sigev_list_mtx = malloc(sizeof(pthread_mutex_t)); + _pthread_mutex_init(sigev_list_mtx, &mattr); + _pthread_mutexattr_destroy(&mattr); + + for (i = 0; i < HASH_QUEUES; ++i) + LIST_INIT(&sigev_hash[i]); + LIST_INIT(&sigev_all); + LIST_INIT(&sigev_threads); + sigev_default_thread = NULL; + if (atfork_registered == 0) { + _pthread_atfork( + __sigev_fork_prepare, + __sigev_fork_parent, + __sigev_fork_child); + atfork_registered = 1; + } + if (!inited) { + _pthread_attr_init(&sigev_default_attr); + _pthread_attr_setscope(&sigev_default_attr, + PTHREAD_SCOPE_SYSTEM); + _pthread_attr_setdetachstate(&sigev_default_attr, + PTHREAD_CREATE_DETACHED); + inited = 1; + } + sigev_default_thread = sigev_thread_create(0); +} + +int +__sigev_check_init(void) +{ + if (!have_threads()) + return (-1); + + _pthread_once(&sigev_once, __sigev_thread_init); + return (sigev_default_thread != NULL) ? 0 : -1; +} + +static void +__sigev_fork_prepare(void) +{ +} + +static void +__sigev_fork_parent(void) +{ +} + +static void +__sigev_fork_child(void) +{ + /* + * This is a hack, the thread libraries really should + * check if the handlers were already registered in + * pthread_atfork(). + */ + atfork_registered = 1; + memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once)); + __sigev_thread_init(); +} + +void +__sigev_list_lock(void) +{ + _pthread_mutex_lock(sigev_list_mtx); +} + +void +__sigev_list_unlock(void) +{ + _pthread_mutex_unlock(sigev_list_mtx); +} + +struct sigev_node * +__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev, + int usedefault) +{ + struct sigev_node *sn; + + sn = calloc(1, sizeof(*sn)); + if (sn != NULL) { + sn->sn_value = evp->sigev_value; + sn->sn_func = evp->sigev_notify_function; + sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1); + sn->sn_type = type; + _pthread_attr_init(&sn->sn_attr); + _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); + if (evp->sigev_notify_attributes) + attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); + if (prev) { + __sigev_list_lock(); + prev->sn_tn->tn_refcount++; + __sigev_list_unlock(); + sn->sn_tn = prev->sn_tn; + } else { + sn->sn_tn = sigev_thread_create(usedefault); + if (sn->sn_tn == NULL) { + _pthread_attr_destroy(&sn->sn_attr); + free(sn); + sn = NULL; + } + } + } + return (sn); +} + +void +__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, + sigev_id_t id) +{ + /* + * Build a new sigevent, and tell kernel to deliver SIGSERVICE + * signal to the new thread. + */ + newevp->sigev_notify = SIGEV_THREAD_ID; + newevp->sigev_signo = SIGSERVICE; + newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; + newevp->sigev_value.sival_ptr = (void *)id; +} + +void +__sigev_free(struct sigev_node *sn) +{ + _pthread_attr_destroy(&sn->sn_attr); + free(sn); +} + +struct sigev_node * +__sigev_find(int type, sigev_id_t id) +{ + struct sigev_node *sn; + int chain = HASH(type, id); + + LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { + if (sn->sn_type == type && sn->sn_id == id) + break; + } + return (sn); +} + +int +__sigev_register(struct sigev_node *sn) +{ + int chain = HASH(sn->sn_type, sn->sn_id); + + LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); + return (0); +} + +int +__sigev_delete(int type, sigev_id_t id) +{ + struct sigev_node *sn; + + sn = __sigev_find(type, id); + if (sn != NULL) + return (__sigev_delete_node(sn)); + return (0); +} + +int +__sigev_delete_node(struct sigev_node *sn) +{ + LIST_REMOVE(sn, sn_link); + + if (--sn->sn_tn->tn_refcount == 0) + _pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE); + if (sn->sn_flags & SNF_WORKING) + sn->sn_flags |= SNF_REMOVED; + else + __sigev_free(sn); + return (0); +} + +static sigev_id_t +sigev_get_id(siginfo_t *si) +{ + switch(si->si_code) { + case SI_TIMER: + return (si->si_timerid); + case SI_MESGQ: + return (si->si_mqd); + case SI_ASYNCIO: + return (sigev_id_t)si->si_value.sival_ptr; + } + return (-1); +} + +static struct sigev_thread * +sigev_thread_create(int usedefault) +{ + struct sigev_thread *tn; + sigset_t set, oset; + int ret; + + if (usedefault && sigev_default_thread) { + __sigev_list_lock(); + sigev_default_thread->tn_refcount++; + __sigev_list_unlock(); + return (sigev_default_thread); + } + + tn = malloc(sizeof(*tn)); + tn->tn_cur = NULL; + tn->tn_lwpid = -1; + tn->tn_refcount = 1; + _pthread_cond_init(&tn->tn_cv, NULL); + + /* for debug */ + __sigev_list_lock(); + LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); + __sigev_list_unlock(); + + sigfillset(&set); /* SIGSERVICE is masked. */ + sigdelset(&set, SIGBUS); + sigdelset(&set, SIGILL); + sigdelset(&set, SIGFPE); + sigdelset(&set, SIGSEGV); + sigdelset(&set, SIGTRAP); + _sigprocmask(SIG_SETMASK, &set, &oset); + ret = _pthread_create(&tn->tn_thread, &sigev_default_attr, + sigev_service_loop, tn); + _sigprocmask(SIG_SETMASK, &oset, NULL); + + if (ret != 0) { + __sigev_list_lock(); + LIST_REMOVE(tn, tn_link); + __sigev_list_unlock(); + free(tn); + tn = NULL; + } else { + /* wait the thread to get its lwpid */ + + __sigev_list_lock(); + while (tn->tn_lwpid == -1) + _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); + __sigev_list_unlock(); + } + return (tn); +} + +/* + * The thread receives notification from kernel and creates + * a thread to call user callback function. + */ +static void * +sigev_service_loop(void *arg) +{ + static int failure; + + siginfo_t si; + sigset_t set; + struct sigev_thread *tn; + struct sigev_node *sn; + sigev_id_t id; + pthread_t td; + int ret; + + tn = arg; + thr_self(&tn->tn_lwpid); + __sigev_list_lock(); + _pthread_cond_broadcast(&tn->tn_cv); + __sigev_list_unlock(); + + sigemptyset(&set); + sigaddset(&set, SIGSERVICE); + for (;;) { + ret = sigwaitinfo(&set, &si); + + __sigev_list_lock(); + if (tn->tn_refcount == 0) { + LIST_REMOVE(tn, tn_link); + __sigev_list_unlock(); + free(tn); + break; + } + + if (ret == -1) { + __sigev_list_unlock(); + continue; + } + + id = sigev_get_id(&si); + sn = __sigev_find(si.si_code, id); + if (sn == NULL) { + __sigev_list_unlock(); + continue; + } + + sn->sn_info = si; + if (sn->sn_flags & SNF_SYNC) + tn->tn_cur = sn; + else + tn->tn_cur = NULL; + sn->sn_flags |= SNF_WORKING; + __sigev_list_unlock(); + + ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn); + if (ret != 0) { + if (failure++ < 5) + warnc(ret, "%s:%s failed to create thread.\n", + __FILE__, __func__); + + __sigev_list_lock(); + sn->sn_flags &= ~SNF_WORKING; + if (sn->sn_flags & SNF_REMOVED) + __sigev_free(sn); + __sigev_list_unlock(); + } else if (tn->tn_cur) { + __sigev_list_lock(); + while (tn->tn_cur) + _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); + __sigev_list_unlock(); + } + } + return (0); +} + +/* + * newly created worker thread to call user callback function. + */ +static void * +worker_routine(void *arg) +{ + struct sigev_node *sn = arg; + + _pthread_cleanup_push(worker_cleanup, sn); + sn->sn_dispatch(sn); + _pthread_cleanup_pop(1); + + return (0); +} + +/* clean up a notification after dispatch. */ +static void +worker_cleanup(void *arg) +{ + struct sigev_node *sn = arg; + + __sigev_list_lock(); + if (sn->sn_flags & SNF_SYNC) { + sn->sn_tn->tn_cur = NULL; + _pthread_cond_broadcast(&sn->sn_tn->tn_cv); + } + if (sn->sn_flags & SNF_REMOVED) + __sigev_free(sn); + else + sn->sn_flags &= ~SNF_WORKING; + __sigev_list_unlock(); +} diff --git a/lib/librt/sigev_thread.h b/lib/librt/sigev_thread.h new file mode 100644 index 0000000..7e30c2a --- /dev/null +++ b/lib/librt/sigev_thread.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef _SIGEV_THREAD_H_ +#define _SIGEV_THREAD_H_ + +#include <sys/types.h> +#include <sys/queue.h> + +struct sigev_thread; +struct sigev_node; + +typedef uintptr_t sigev_id_t; +typedef void (*sigev_dispatch_t)(struct sigev_node *); + +struct sigev_node { + LIST_ENTRY(sigev_node) sn_link; + int sn_type; + sigev_id_t sn_id; + sigev_dispatch_t sn_dispatch; + union sigval sn_value; + void *sn_func; + int sn_flags; + int sn_gen; + siginfo_t sn_info; + pthread_attr_t sn_attr; + struct sigev_thread *sn_tn; +}; + + +struct sigev_thread { + LIST_ENTRY(sigev_thread) tn_link; + pthread_t tn_thread; + struct sigev_node *tn_cur; + int tn_refcount; + long tn_lwpid; + pthread_cond_t tn_cv; +}; + +#define SNF_WORKING 0x01 +#define SNF_REMOVED 0x02 +#define SNF_SYNC 0x04 + +#define SIGSERVICE (SIGTHR+1) + +int __sigev_check_init(); +struct sigev_node *__sigev_alloc(int, const struct sigevent *, + struct sigev_node *, int); +struct sigev_node *__sigev_find(int, sigev_id_t); +void __sigev_get_sigevent(struct sigev_node *, struct sigevent *, + sigev_id_t); +int __sigev_register(struct sigev_node *); +int __sigev_delete(int, sigev_id_t); +int __sigev_delete_node(struct sigev_node *); +void __sigev_list_lock(void); +void __sigev_list_unlock(void); +void __sigev_free(struct sigev_node *); + +#endif diff --git a/lib/librt/timer.c b/lib/librt/timer.c new file mode 100644 index 0000000..f02e761 --- /dev/null +++ b/lib/librt/timer.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2006 David Xu <davidxu@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/syscall.h> + +#include "namespace.h" +#include <errno.h> +#include <stddef.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include "sigev_thread.h" +#include "un-namespace.h" + +extern int __sys_ktimer_create(clockid_t, struct sigevent *__restrict, + int *__restrict); +extern int __sys_ktimer_delete(int); +extern int __sys_ktimer_gettime(int, struct itimerspec *); +extern int __sys_ktimer_getoverrun(int); +extern int __sys_ktimer_settime(int, int, + const struct itimerspec *__restrict, struct itimerspec *__restrict); + +struct __timer { + int oshandle; + struct sigev_node *node; +}; + +__weak_reference(__timer_create, timer_create); +__weak_reference(__timer_create, _timer_create); +__weak_reference(__timer_delete, timer_delete); +__weak_reference(__timer_delete, _timer_delete); +__weak_reference(__timer_gettime, timer_gettime); +__weak_reference(__timer_gettime, _timer_gettime); +__weak_reference(__timer_settime, timer_settime); +__weak_reference(__timer_settime, _timer_settime); +__weak_reference(__timer_getoverrun, timer_getoverrun); +__weak_reference(__timer_getoverrun, _timer_getoverrun); + +typedef void (*timer_func)(union sigval val, int overrun); + +static void +timer_dispatch(struct sigev_node *sn) +{ + timer_func f = sn->sn_func; + + /* I want to avoid expired notification. */ + if (sn->sn_info.si_value.sival_int == sn->sn_gen) + f(sn->sn_value, sn->sn_info.si_overrun); +} + +int +__timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) +{ + struct __timer *timer; + struct sigevent ev; + struct sigev_node *sn; + int ret, err; + + timer = malloc(sizeof(struct __timer)); + if (timer == NULL) + return (-1); + + if (evp == NULL || evp->sigev_notify != SIGEV_THREAD) { + ret = __sys_ktimer_create(clockid, evp, &timer->oshandle); + if (ret == -1) { + err = errno; + free(timer); + errno = err; + return (ret); + } + timer->node = NULL; + *timerid = timer; + return (0); + } + + if (__sigev_check_init()) { + errno = EINVAL; + return (-1); + } + + sn = __sigev_alloc(SI_TIMER, evp, NULL, 0); + if (sn == NULL) { + errno = EAGAIN; + return (-1); + } + + __sigev_get_sigevent(sn, &ev, sn->sn_gen); + ret = __sys_ktimer_create(clockid, &ev, &timer->oshandle); + if (ret != 0) { + err = errno; + __sigev_free(sn); + free(timer); + errno = err; + return (-1); + } + sn->sn_flags |= SNF_SYNC; + sn->sn_dispatch = timer_dispatch; + sn->sn_id = timer->oshandle; + timer->node = sn; + __sigev_list_lock(); + __sigev_register(sn); + __sigev_list_unlock(); + *timerid = timer; + return (0); +} + +int +__timer_delete(timer_t timerid) +{ + int ret, err; + + if (timerid->node != NULL) { + __sigev_list_lock(); + __sigev_delete_node(timerid->node); + __sigev_list_unlock(); + } + ret = __sys_ktimer_delete(timerid->oshandle); + err = errno; + free(timerid); + errno = err; + return (ret); +} + +int +__timer_gettime(timer_t timerid, struct itimerspec *value) +{ + + return __sys_ktimer_gettime(timerid->oshandle, value); +} + +int +__timer_getoverrun(timer_t timerid) +{ + + return __sys_ktimer_getoverrun(timerid->oshandle); +} + +int +__timer_settime(timer_t timerid, int flags, + const struct itimerspec *__restrict value, + struct itimerspec *__restrict ovalue) +{ + + return __sys_ktimer_settime(timerid->oshandle, + flags, value, ovalue); +} + +int +__timer_oshandle(timer_t timerid) +{ + + return (timerid->oshandle); +} |