xref: /freebsd/lib/librt/timer.c (revision 4acaec8f)
14acaec8fSDavid Xu /*
24acaec8fSDavid Xu  * Copyright (c) 2006 David Xu <davidxu@freebsd.org>
34acaec8fSDavid Xu  * All rights reserved.
44acaec8fSDavid Xu  *
54acaec8fSDavid Xu  * Redistribution and use in source and binary forms, with or without
64acaec8fSDavid Xu  * modification, are permitted provided that the following conditions
74acaec8fSDavid Xu  * are met:
84acaec8fSDavid Xu  * 1. Redistributions of source code must retain the above copyright
94acaec8fSDavid Xu  *    notice unmodified, this list of conditions, and the following
104acaec8fSDavid Xu  *    disclaimer.
114acaec8fSDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
124acaec8fSDavid Xu  *    notice, this list of conditions and the following disclaimer in the
134acaec8fSDavid Xu  *    documentation and/or other materials provided with the distribution.
144acaec8fSDavid Xu  *
154acaec8fSDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
164acaec8fSDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
174acaec8fSDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
184acaec8fSDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
194acaec8fSDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
204acaec8fSDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
214acaec8fSDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
224acaec8fSDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
234acaec8fSDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
244acaec8fSDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
254acaec8fSDavid Xu  *
264acaec8fSDavid Xu  * $FreeBSD$
274acaec8fSDavid Xu  *
284acaec8fSDavid Xu  */
294acaec8fSDavid Xu 
304acaec8fSDavid Xu #include <sys/cdefs.h>
314acaec8fSDavid Xu #include <sys/types.h>
324acaec8fSDavid Xu #include <sys/syscall.h>
334acaec8fSDavid Xu 
344acaec8fSDavid Xu #include "namespace.h"
354acaec8fSDavid Xu #include <errno.h>
364acaec8fSDavid Xu #include <pthread.h>
374acaec8fSDavid Xu #include <stddef.h>
384acaec8fSDavid Xu #include <signal.h>
394acaec8fSDavid Xu #include <stdlib.h>
404acaec8fSDavid Xu #include <time.h>
414acaec8fSDavid Xu #include <unistd.h>
424acaec8fSDavid Xu #include "sigev_thread.h"
434acaec8fSDavid Xu #include "un-namespace.h"
444acaec8fSDavid Xu 
454acaec8fSDavid Xu extern int __sys_ktimer_create(clockid_t, struct sigevent *__restrict,
464acaec8fSDavid Xu 	int *__restrict);
474acaec8fSDavid Xu extern int __sys_ktimer_delete(int);
484acaec8fSDavid Xu extern int __sys_ktimer_gettime(int, struct itimerspec *);
494acaec8fSDavid Xu extern int __sys_ktimer_getoverrun(int);
504acaec8fSDavid Xu extern int __sys_ktimer_settime(int, int,
514acaec8fSDavid Xu 	const struct itimerspec *__restrict, struct itimerspec *__restrict);
524acaec8fSDavid Xu 
534acaec8fSDavid Xu struct __timer {
544acaec8fSDavid Xu 	int oshandle;
554acaec8fSDavid Xu 	struct sigev_node *node;
564acaec8fSDavid Xu };
574acaec8fSDavid Xu 
584acaec8fSDavid Xu __weak_reference(__timer_create, timer_create);
594acaec8fSDavid Xu __weak_reference(__timer_create, _timer_create);
604acaec8fSDavid Xu __weak_reference(__timer_delete, timer_delete);
614acaec8fSDavid Xu __weak_reference(__timer_delete, _timer_delete);
624acaec8fSDavid Xu __weak_reference(__timer_gettime, timer_gettime);
634acaec8fSDavid Xu __weak_reference(__timer_gettime, _timer_gettime);
644acaec8fSDavid Xu __weak_reference(__timer_settime, timer_settime);
654acaec8fSDavid Xu __weak_reference(__timer_settime, _timer_settime);
664acaec8fSDavid Xu __weak_reference(__timer_getoverrun, timer_getoverrun);
674acaec8fSDavid Xu __weak_reference(__timer_getoverrun, _timer_getoverrun);
684acaec8fSDavid Xu 
694acaec8fSDavid Xu typedef void (*timer_func)(union sigval val, int timerid, int overrun);
704acaec8fSDavid Xu 
714acaec8fSDavid Xu static void
724acaec8fSDavid Xu timer_dispatch(struct sigev_node *sn, siginfo_t *si)
734acaec8fSDavid Xu {
744acaec8fSDavid Xu 	timer_func f = sn->sn_func;
754acaec8fSDavid Xu 
764acaec8fSDavid Xu 	/* I want to avoid expired notification. */
774acaec8fSDavid Xu 	if (si->si_value.sival_int == sn->sn_gen)
784acaec8fSDavid Xu 		f(sn->sn_value, si->si_timerid, si->si_overrun);
794acaec8fSDavid Xu }
804acaec8fSDavid Xu 
814acaec8fSDavid Xu int
824acaec8fSDavid Xu __timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid)
834acaec8fSDavid Xu {
844acaec8fSDavid Xu 	struct __timer *timer;
854acaec8fSDavid Xu 	struct sigevent ev;
864acaec8fSDavid Xu 	struct sigev_node *sn;
874acaec8fSDavid Xu 	int ret, err;
884acaec8fSDavid Xu 
894acaec8fSDavid Xu 	timer = malloc(sizeof(struct __timer));
904acaec8fSDavid Xu 	if (timer == NULL)
914acaec8fSDavid Xu 		return (-1);
924acaec8fSDavid Xu 
934acaec8fSDavid Xu 	if (evp == NULL || evp->sigev_notify != SIGEV_THREAD) {
944acaec8fSDavid Xu 		ret = __sys_ktimer_create(clockid, evp, &timer->oshandle);
954acaec8fSDavid Xu 		if (ret == -1) {
964acaec8fSDavid Xu 			err = errno;
974acaec8fSDavid Xu 			free(timer);
984acaec8fSDavid Xu 			errno = err;
994acaec8fSDavid Xu 			return (ret);
1004acaec8fSDavid Xu 		}
1014acaec8fSDavid Xu 		timer->node = NULL;
1024acaec8fSDavid Xu 		*timerid = timer;
1034acaec8fSDavid Xu 		return (0);
1044acaec8fSDavid Xu 	}
1054acaec8fSDavid Xu 
1064acaec8fSDavid Xu 	if (__sigev_check_init()) {
1074acaec8fSDavid Xu 		errno = EINVAL;
1084acaec8fSDavid Xu 		return (-1);
1094acaec8fSDavid Xu 	}
1104acaec8fSDavid Xu 
1114acaec8fSDavid Xu 	sn = __sigev_alloc(SI_TIMER, evp);
1124acaec8fSDavid Xu 	if (sn == NULL) {
1134acaec8fSDavid Xu 		errno = EAGAIN;
1144acaec8fSDavid Xu 		return (-1);
1154acaec8fSDavid Xu 	}
1164acaec8fSDavid Xu 
1174acaec8fSDavid Xu 	__sigev_get_sigevent(sn, &ev, sn->sn_gen);
1184acaec8fSDavid Xu 	ret = __sys_ktimer_create(clockid, &ev, &timer->oshandle);
1194acaec8fSDavid Xu 	if (ret != 0) {
1204acaec8fSDavid Xu 		err = errno;
1214acaec8fSDavid Xu 		__sigev_free(sn);
1224acaec8fSDavid Xu 		free(timer);
1234acaec8fSDavid Xu 		errno = err;
1244acaec8fSDavid Xu 		return (-1);
1254acaec8fSDavid Xu 	}
1264acaec8fSDavid Xu 	sn->sn_dispatch = timer_dispatch;
1274acaec8fSDavid Xu 	sn->sn_id = timer->oshandle;
1284acaec8fSDavid Xu 	__sigev_list_lock();
1294acaec8fSDavid Xu 	__sigev_register(sn);
1304acaec8fSDavid Xu 	__sigev_list_unlock();
1314acaec8fSDavid Xu 	*timerid = timer;
1324acaec8fSDavid Xu 	return (0);
1334acaec8fSDavid Xu }
1344acaec8fSDavid Xu 
1354acaec8fSDavid Xu int
1364acaec8fSDavid Xu __timer_delete(timer_t timerid)
1374acaec8fSDavid Xu {
1384acaec8fSDavid Xu 	int ret, err;
1394acaec8fSDavid Xu 
1404acaec8fSDavid Xu 	if (timerid->node != NULL) {
1414acaec8fSDavid Xu 		__sigev_list_lock();
1424acaec8fSDavid Xu 		__sigev_delete_node(timerid->node);
1434acaec8fSDavid Xu 		__sigev_list_unlock();
1444acaec8fSDavid Xu 	}
1454acaec8fSDavid Xu 	ret = __sys_ktimer_delete(timerid->oshandle);
1464acaec8fSDavid Xu 	err = errno;
1474acaec8fSDavid Xu 	free(timerid);
1484acaec8fSDavid Xu 	errno = err;
1494acaec8fSDavid Xu 	return (ret);
1504acaec8fSDavid Xu }
1514acaec8fSDavid Xu 
1524acaec8fSDavid Xu int
1534acaec8fSDavid Xu __timer_gettime(timer_t timerid, struct itimerspec *value)
1544acaec8fSDavid Xu {
1554acaec8fSDavid Xu 
1564acaec8fSDavid Xu 	return __sys_ktimer_gettime(timerid->oshandle, value);
1574acaec8fSDavid Xu }
1584acaec8fSDavid Xu 
1594acaec8fSDavid Xu int
1604acaec8fSDavid Xu __timer_getoverrun(timer_t timerid)
1614acaec8fSDavid Xu {
1624acaec8fSDavid Xu 
1634acaec8fSDavid Xu 	return __sys_ktimer_getoverrun(timerid->oshandle);
1644acaec8fSDavid Xu }
1654acaec8fSDavid Xu 
1664acaec8fSDavid Xu int
1674acaec8fSDavid Xu __timer_settime(timer_t timerid, int flags,
1684acaec8fSDavid Xu 	const struct itimerspec *__restrict value,
1694acaec8fSDavid Xu 	struct itimerspec *__restrict ovalue)
1704acaec8fSDavid Xu {
1714acaec8fSDavid Xu 
1724acaec8fSDavid Xu 	return __sys_ktimer_settime(timerid->oshandle,
1734acaec8fSDavid Xu 		flags, value, ovalue);
1744acaec8fSDavid Xu }
1754acaec8fSDavid Xu 
1764acaec8fSDavid Xu int
1774acaec8fSDavid Xu __timer_oshandle(timer_t timerid)
1784acaec8fSDavid Xu {
1794acaec8fSDavid Xu 
1804acaec8fSDavid Xu 	return (timerid->oshandle);
1814acaec8fSDavid Xu }
182