1
0
Fork 0
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:
Steffen Vogel 2017-08-14 14:35:16 +02:00
parent a219343fcd
commit a13097fa80
9 changed files with 246 additions and 85 deletions

View file

@ -33,6 +33,7 @@
#include "advio.h"
#include "node.h"
#include "periodic_task.h"
#define FILE_MAX_PATHLEN 512

View file

@ -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 */

View file

@ -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 */

View 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);

View file

@ -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);

View file

@ -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;

View file

@ -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
View 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;
}

View file

@ -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;