You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.0 KiB
C++

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
/*! \brief Common helper function for epoll
*
*/
#ifndef SRSRAN_EPOLL_HELPER_H
#define SRSRAN_EPOLL_HELPER_H
#include <functional>
#include <signal.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
///< A virtual interface to handle epoll events (used by timer and port handler)
class epoll_handler
{
public:
virtual int handle_event(int fd, epoll_event e, int epoll_fd) = 0;
};
///< Callback function called when timer expires
using epoll_timer_callback = std::function<void(uint64_t res)>;
///< Epoll timer handler
class epoll_timer_handler : public epoll_handler
{
public:
epoll_timer_handler(int fd_, epoll_timer_callback callback_) : timer_fd(fd_), callback(callback_){};
int handle_event(int fd, epoll_event e, int epoll_fd)
{
uint64_t res;
int ret = read(fd, &res, sizeof(res));
callback(res);
return ret;
}
int get_timer_fd() { return timer_fd; };
private:
int timer_fd = -1;
epoll_timer_callback callback;
};
///< Basic epoll signal handler
class epoll_signal_handler : public epoll_handler
{
public:
epoll_signal_handler(bool* running_) : running(running_) {}
int handle_event(int fd, epoll_event e, int epoll_fd)
{
struct signalfd_siginfo info;
if (read(fd, &info, sizeof(info)) != sizeof(info)) {
fprintf(stderr, "failed to read signal fd buffer\n");
return SRSRAN_ERROR;
}
switch (info.ssi_signo) {
case SIGTERM:
case SIGINT:
case SIGHUP:
case SIGQUIT:
*running = false;
break;
default:
fprintf(stderr, "got signal %d\n", info.ssi_signo);
break;
}
return SRSRAN_SUCCESS;
}
private:
bool* running = nullptr;
};
///< Create periodic epoll timer every 1ms
inline int create_tti_timer()
{
int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timer_fd == -1) {
printf("timerfd_create() failed: errno=%d\n", errno);
return SRSRAN_ERROR;
}
int msec = 1; // our 1ms TTI timer
struct itimerspec ts = {};
ts.it_value.tv_sec = msec / 1000;
ts.it_value.tv_nsec = (msec % 1000) * 1000000;
ts.it_interval.tv_sec = msec / 1000;
ts.it_interval.tv_nsec = (msec % 1000) * 1000000;
if (timerfd_settime(timer_fd, 0, &ts, NULL) < 0) {
printf("timerfd_settime() failed: errno=%d\n", errno);
close(timer_fd);
return SRSRAN_ERROR;
}
return timer_fd;
}
///< Blocks all signals from the calling thread
inline void block_signals()
{
// block all signals. we take signals synchronously via signalfd
sigset_t all;
sigfillset(&all);
pthread_sigmask(SIG_BLOCK, &all, NULL);
}
///< Create signalfd for handling signals
inline int add_signalfd()
{
// add signals we accept synchronously via signalfd
std::vector<int> sigs = {SIGIO, SIGHUP, SIGTERM, SIGINT, SIGQUIT, SIGALRM};
sigset_t sw;
sigemptyset(&sw);
for (auto& sig : sigs) {
sigaddset(&sw, sig);
}
// create the signalfd for receiving signals
int sig_fd = signalfd(-1, &sw, 0);
if (sig_fd == -1) {
fprintf(stderr, "signalfd: %s\n", strerror(errno));
return SRSRAN_ERROR;
}
return sig_fd;
}
///< Add fd to epoll fd
inline int add_epoll(int fd, int epoll_fd)
{
struct epoll_event ev = {};
ev.data.fd = fd;
ev.events = EPOLLIN;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
fprintf(stderr, "epoll_ctl failed for fd=%d\n", fd);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
///< Remove fd from epoll
inline int del_epoll(int fd, int epoll_fd)
{
struct epoll_event ev = {};
ev.data.fd = fd;
ev.events = EPOLLIN;
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev) == -1) {
fprintf(stderr, "epoll_ctl failed for fd=%d\n", fd);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
#endif // SRSRAN_EPOLL_HELPER_H