/** * * \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 #include #include #include #include #include #include ///< 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; ///< 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(std::atomic& 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: std::atomic& running; }; ///< 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 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