diff --git a/include/villas/nodes/file.h b/include/villas/nodes/file.h index 50e5667fd..ba489f439 100644 --- a/include/villas/nodes/file.h +++ b/include/villas/nodes/file.h @@ -33,6 +33,7 @@ #include "advio.h" #include "node.h" +#include "periodic_task.h" #define FILE_MAX_PATHLEN 512 diff --git a/include/villas/nodes/ngsi.h b/include/villas/nodes/ngsi.h index bd9c4a82d..b48547489 100644 --- a/include/villas/nodes/ngsi.h +++ b/include/villas/nodes/ngsi.h @@ -41,6 +41,7 @@ #include "list.h" #include "super_node.h" #include "node.h" +#include "periodic_task.h" struct node; @@ -53,7 +54,7 @@ struct ngsi { double timeout; /**< HTTP timeout in seconds */ double rate; /**< Rate used for polling. */ - int tfd; /**< Timer */ + struct periodic_task timer; /**< Timer for periodic events. */ int ssl_verify; /**< Boolean flag whether SSL server certificates should be verified or not. */ struct curl_slist *headers; /**< List of HTTP request headers for libcurl */ diff --git a/include/villas/nodes/signal.h b/include/villas/nodes/signal.h index 106b52a43..78290d379 100644 --- a/include/villas/nodes/signal.h +++ b/include/villas/nodes/signal.h @@ -30,6 +30,7 @@ #pragma once #include "timing.h" +#include "periodic_task.h" /* Forward declarations */ struct node; @@ -48,7 +49,7 @@ enum signal_type { * @see node_type */ struct signal { - int tfd; /**< timerfd file descriptor. */ + struct periodic_task timer; /**< Timer for periodic events. */ int rt; /**< Real-time mode? */ enum signal_type type; /**< Signal type */ diff --git a/include/villas/periodic_task.h b/include/villas/periodic_task.h new file mode 100644 index 000000000..4239e1616 --- /dev/null +++ b/include/villas/periodic_task.h @@ -0,0 +1,70 @@ +/** Run tasks periodically. + * + * @file + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * 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 + * 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 . + *********************************************************************************/ + +#pragma once + +#include +#include + +#include + +/** We can choose between two periodic timer implementations */ +//#define PERIODIC_TASK_IMPL NANOSLEEP +#define TIMERFD 1 +#define CLOCK_NANOSLEEP 2 +#define NANOSLEEP 3 + +#if defined(__MACH__) + #define PERIODIC_TASK_IMPL NANOSLEEP +#else + #define PERIODIC_TASK_IMPL CLOCK_NANOSLEEP +#endif + +struct periodic_task { + struct timespec period; +#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP + struct timespec next_period; +#elif PERIODIC_TASK_IMPL == TIMERFD + int fd; +#else + #error "Invalid period task implementation" +#endif +}; + +/** Create a new timer with the given rate. */ +int periodic_task_init(struct periodic_task *t, double rate); + +int periodic_task_destroy(struct periodic_task *t); + +/** Wait until timer elapsed + * + * @retval 0 An error occured. Maybe the timer was stopped. + * @retval >0 The nummer of runs this timer already fired. + */ +uint64_t periodic_task_wait_until_next_period(struct periodic_task *t); + +/** Wait until a fixed time in the future is reached + * + * @param until A pointer to a time in the future. + */ +int periodic_task_wait_until(struct periodic_task *t, const struct timespec *until); \ No newline at end of file diff --git a/include/villas/timing.h b/include/villas/timing.h index 6cb00c19e..1ef49e123 100644 --- a/include/villas/timing.h +++ b/include/villas/timing.h @@ -28,30 +28,6 @@ #include -#ifdef __linux__ - #include -#endif - -/** Create a new timer with the given rate. */ -int timerfd_create_rate(double rate); - -/** Wait until timer elapsed - * - * @param fd A file descriptor which was created by timerfd_create(3). - * @retval 0 An error occured. Maybe the timer was stopped. - * @retval >0 The nummer of runs this timer already fired. - */ -uint64_t timerfd_wait(int fd); - -/** Wait until a fixed time in the future is reached - * - * @param fd A file descriptor which was created by timerfd_create(3). - * @param until A pointer to a time in the future. - * @retval 0 An error occured. Maybe the timer was stopped. - * @retval >0 The nummer of runs this timer already fired. - */ -uint64_t timerfd_wait_until(int fd, const struct timespec *until); - /** Get delta between two timespec structs */ struct timespec time_diff(const struct timespec *start, const struct timespec *end); diff --git a/lib/nodes/ngsi.c b/lib/nodes/ngsi.c index 2bc3f2cc8..e87eb8ac9 100644 --- a/lib/nodes/ngsi.c +++ b/lib/nodes/ngsi.c @@ -492,8 +492,8 @@ int ngsi_start(struct node *n) if (i->timeout > 1 / i->rate) warn("Timeout is to large for given rate: %f", i->rate); - i->tfd = timerfd_create_rate(i->rate); - if (i->tfd < 0) + ret = periodic_task_init(&i->timer, i->rate); + if (ret) serror("Failed to create timer"); i->headers = curl_slist_append(i->headers, "Accept: application/json"); @@ -539,7 +539,7 @@ int ngsi_read(struct node *n, struct sample *smps[], unsigned cnt) struct ngsi *i = n->_vd; int ret; - if (timerfd_wait(i->tfd) == 0) + if (periodic_task_wait_until_next_period(&i->timer) == 0) perror("Failed to wait for timer"); json_t *rentity; diff --git a/lib/nodes/signal.c b/lib/nodes/signal.c index 77da72999..61239cca6 100644 --- a/lib/nodes/signal.c +++ b/lib/nodes/signal.c @@ -157,6 +157,7 @@ check: if (optarg == endptr) int signal_open(struct node *n) { + int ret; struct signal *s = n->_vd; s->counter = 0; @@ -164,22 +165,25 @@ int signal_open(struct node *n) /* Setup timer */ if (s->rt) { - s->tfd = timerfd_create_rate(s->rate); - if (s->tfd < 0) - return -1; + ret = periodic_task_init(&s->timer, s->rate); + if (ret) + return ret; } - else - s->tfd = -1; return 0; } int signal_close(struct node *n) { + int ret; struct signal* s = n->_vd; - close(s->tfd); - + if (s->rt) { + ret = periodic_task_destroy(&s->timer); + if (ret) + return ret; + } + return 0; } @@ -196,7 +200,7 @@ int signal_read(struct node *n, struct sample *smps[], unsigned cnt) /* Throttle output if desired */ if (s->rt) { /* Block until 1/p->rate seconds elapsed */ - steps = timerfd_wait(s->tfd); + steps = periodic_task_wait_until_next_period(&s->timer); if (steps > 1) warn("Missed steps: %u", steps); diff --git a/lib/periodic_task.c b/lib/periodic_task.c new file mode 100644 index 000000000..7fcde99cc --- /dev/null +++ b/lib/periodic_task.c @@ -0,0 +1,156 @@ +/** Run tasks periodically. + * + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * 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 + * 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 +#include +#include + +#include "utils.h" + +#include "periodic_task.h" +#include "timing.h" + +#if PERIODIC_TASK_IMPL == TIMERFD + #include +#endif + +int periodic_task_init(struct periodic_task *t, double rate) +{ + t->period = time_from_double(1.0 / rate); + +#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); + + t->next_period = time_add(&now, &t->period); +#elif PERIODIC_TASK_IMPL == TIMERFD + int ret; + + struct itimerspec its = { + .it_interval = t->period, + .it_value = t->period + }; + + t->fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (t->fd < 0) + return -1; + + ret = timerfd_settime(t->fd, 0, &its, NULL); + if (ret) + return ret; +#else + #error "Invalid period task implementation" +#endif + + return 0; +} + +int periodic_task_destroy(struct periodic_task *t) +{ +#if PERIODIC_TASK_IMPL == TIMERFD + return close(t->fd); +#endif + + return 0; +} + +#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP +static int time_lt(const struct timespec *lhs, const struct timespec *rhs) +{ + if (lhs->tv_sec == rhs->tv_sec) + return lhs->tv_nsec < rhs->tv_nsec; + else + return lhs->tv_sec < rhs->tv_sec; + + return 0; +} +#endif + +uint64_t periodic_task_wait_until_next_period(struct periodic_task *t) +{ + uint64_t runs; + int ret; + +#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP + ret = periodic_task_wait_until(t, &t->next_period); + + struct timespec now; + + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret) + return 0; + + for (runs = 0; time_lt(&t->next_period, &now); runs++) + t->next_period = time_add(&t->next_period, &t->period); + +#elif PERIODIC_TASK_IMPL == TIMERFD + ret = read(t->fd, &runs, sizeof(runs)); + if (ret < 0) + return 0; +#else + #error "Invalid period task implementation" +#endif + + return runs; +} + + +int periodic_task_wait_until(struct periodic_task *t, const struct timespec *until) +{ + int ret; + +#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP +retry: ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, until, NULL); + if (ret == EINTR) + goto retry; +#elif PERIODIC_TASK_IMPL == NANOSLEEP + struct timespec now, delta; + + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret) + return ret; + + delta = time_diff(&now, until); + + ret = nanosleep(&delta, NULL); +#elif PERIODIC_TASK_IMPL == TIMERFD + uint64_t runs; + + struct itimerspec its = { + .it_value = *until, + .it_interval = { 0, 0 } + }; + + ret = timerfd_settime(t->fd, TFD_TIMER_ABSTIME, &its, NULL); + if (ret) + return 0; + + ret = read(t->fd, &runs, sizeof(runs)); + if (ret < 0) + return 0; +#else + #error "Invalid period task implementation" +#endif + + return ret; +} \ No newline at end of file diff --git a/lib/timing.c b/lib/timing.c index ab06f9569..f637e3d91 100644 --- a/lib/timing.c +++ b/lib/timing.c @@ -24,54 +24,6 @@ #include "timing.h" -#ifdef __linux__ - -int timerfd_create_rate(double rate) -{ - int fd, ret; - - struct timespec ts = time_from_double(1 / rate); - - struct itimerspec its = { - .it_interval = ts, - .it_value = ts - }; - - fd = timerfd_create(CLOCK_MONOTONIC, 0); - if (fd < 0) - return fd; - - ret = timerfd_settime(fd, 0, &its, NULL); - if (ret) - return ret; - - return fd; -} - -uint64_t timerfd_wait(int fd) -{ - uint64_t runs; - - return read(fd, &runs, sizeof(runs)) < 0 ? 0 : runs; -} - -uint64_t timerfd_wait_until(int fd, const struct timespec *until) -{ - int ret; - struct itimerspec its = { - .it_value = *until, - .it_interval = { 0, 0 } - }; - - ret = timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, NULL); - if (ret) - return 0; - - return timerfd_wait(fd); -} - -#endif /* __linux__ */ - struct timespec time_now() { struct timespec ts;