diff options
author | davidxu <davidxu@FreeBSD.org> | 2005-10-26 11:08:32 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2005-10-26 11:08:32 +0000 |
commit | 3aca9ad9f972107cab20c80274f830f4c9e58ff9 (patch) | |
tree | 7ad18f7da8679ae8f22836cbfce933d23566a178 /lib/libthr/thread/thr_timer.c | |
parent | d9ad5313fd8fecf625cb947524ce3d56da1e7600 (diff) | |
download | FreeBSD-src-3aca9ad9f972107cab20c80274f830f4c9e58ff9.zip FreeBSD-src-3aca9ad9f972107cab20c80274f830f4c9e58ff9.tar.gz |
Add experiment code to implement POSIX timer's SIGEV_THREAD notification.
Diffstat (limited to 'lib/libthr/thread/thr_timer.c')
-rw-r--r-- | lib/libthr/thread/thr_timer.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/lib/libthr/thread/thr_timer.c b/lib/libthr/thread/thr_timer.c new file mode 100644 index 0000000..fa3b893 --- /dev/null +++ b/lib/libthr/thread/thr_timer.c @@ -0,0 +1,168 @@ +/* + * 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 <time.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/signalvar.h> + +#include "thr_private.h" + +struct timer { + int signo; + union sigval value; + void (*function)(union sigval *); + int exit; + int timerid; + umtx_t lock; +}; + +extern int __sys_timer_create(clockid_t clockid, struct sigevent *evp, + timer_t *timerid); + +static void *timer_loop(void *arg); + +__weak_reference(__timer_create, timer_create); +__weak_reference(__timer_create, _timer_create); + +#define SIGTIMER SIGCANCEL /* Reuse SIGCANCEL */ + +/* + * Purpose of the function is to implement POSIX timer's + * SEGEV_THREAD notification mechanism. + */ +int +__timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) +{ + struct pthread *curthread = _get_curthread(); + pthread_attr_t attr; + struct sigevent ev; + struct timer *tmr; + pthread_t newtd; + sigset_t set, oset; + int ret; + + /* Call syscall directly if it is not SIGEV_THREAD */ + if (evp == NULL || evp->sigev_notify != SIGEV_THREAD) + return (__sys_timer_create(clockid, evp, timerid)); + + /* Otherwise, do all magical things. */ + tmr = malloc(sizeof(*tmr)); + if (__predict_false(tmr == NULL)) { + errno = EAGAIN; + return (-1); + } + tmr->signo = SIGTIMER; + tmr->value = evp->sigev_value; + tmr->function = evp->sigev_notify_function; + tmr->exit = 0; + tmr->timerid = -1; + _thr_umtx_init(&tmr->lock); + pthread_attr_init(&attr); + if (evp->sigev_notify_attributes != NULL) { + *attr = **(pthread_attr_t *)(evp->sigev_notify_attributes); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + } + /* + * Lock the mutex, so new thread can not continue until + * we have fully setup it. + */ + THR_UMTX_LOCK(curthread, &tmr->lock); + + /* + * Block signal the timer will fire for new thread, new thread + * will use sigwaitinfo, signal action should not be invoked. + * + * Application user: if you want set signal mask for the + * background thread, please call sigprocmask for current before + * calling timer_create, this way, the signal mask will be inherited + * by new thread. + */ + SIGEMPTYSET(set); + SIGADDSET(set, tmr->signo); + __sys_sigprocmask(SIG_BLOCK, &set, &oset); + ret = pthread_create(&newtd, &attr, timer_loop, tmr); + __sys_sigprocmask(SIG_SETMASK, &oset, NULL); + pthread_attr_destroy(&attr); + if (__predict_false(ret != 0)) { + THR_UMTX_UNLOCK(curthread, &tmr->lock); + free(tmr); + errno = ret; + return (-1); + } + /* + * Build a new sigevent, and tell kernel to deliver + * SIGTIMER signal to the new thread. + */ + ev.sigev_notify = SIGEV_THREAD_ID; + ev.sigev_signo = tmr->signo; + ev.sigev_notify_thread_id = (int)newtd->tid; + ev.sigev_value.sigval_ptr = tmr; + ret = __sys_timer_create(clockid, &ev, timerid); + if (ret != 0) { + ret = errno; + tmr->exit = 1; + THR_UMTX_UNLOCK(curthread, &tmr->lock); + pthread_join(newtd, NULL); + errno = ret; + return (-1); + } + tmr->timerid = *timerid; + /* + * As specification says, the service thread should run in + * detached state, so you lose control of the thread! + */ + pthread_detach(newtd); + THR_UMTX_UNLOCK(curthread, &tmr->lock); + return (0); +} + +/* Thread function to serve SEGEV_THREAD notifcation. */ +static void * +timer_loop(void *arg) +{ + struct pthread *curthread = _get_curthread(); + struct timer *tmr = arg; + siginfo_t si; + sigset_t set; + + THR_UMTX_LOCK(curthread, &tmr->lock); + THR_UMTX_UNLOCK(curthread, &tmr->lock); + SIGEMPTYSET(set); + SIGADDSET(set, tmr->signo); + while (tmr->exit == 0) { + if (__sys_sigwaitinfo(&set, &si) != -1) { + if (si.si_code == SI_TIMER && + si.si_timerid == tmr->timerid) + tmr->function(&tmr->value); + } + } + free(tmr); + return (0); +} |