mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
add new class to abstract different implementations for periodic tasks
This commit is contained in:
parent
a219343fcd
commit
a13097fa80
9 changed files with 246 additions and 85 deletions
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "advio.h"
|
||||
#include "node.h"
|
||||
#include "periodic_task.h"
|
||||
|
||||
#define FILE_MAX_PATHLEN 512
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
70
include/villas/periodic_task.h
Normal file
70
include/villas/periodic_task.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/** Run tasks periodically.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/** 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);
|
|
@ -28,30 +28,6 @@
|
|||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/timerfd.h>
|
||||
#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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
156
lib/periodic_task.c
Normal file
156
lib/periodic_task.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/** Run tasks periodically.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "periodic_task.h"
|
||||
#include "timing.h"
|
||||
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
#include <sys/timerfd.h>
|
||||
#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;
|
||||
}
|
48
lib/timing.c
48
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;
|
||||
|
|
Loading…
Add table
Reference in a new issue