summaryrefslogtreecommitdiffstats
path: root/lib/librt
diff options
context:
space:
mode:
Diffstat (limited to 'lib/librt')
-rw-r--r--lib/librt/Makefile16
-rw-r--r--lib/librt/Version.map69
-rw-r--r--lib/librt/aio.c196
-rw-r--r--lib/librt/mq.c232
-rw-r--r--lib/librt/sigev_thread.c465
-rw-r--r--lib/librt/sigev_thread.h85
-rw-r--r--lib/librt/timer.c181
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);
+}
OpenPOWER on IntegriCloud