Skip to content

Instantly share code, notes, and snippets.

@oneman
Forked from dsheeler/gist:9560509
Last active August 29, 2015 13:57
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/signalfd.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#define KR_EPOLL_MAX_EVENTS 10
static void handle_error(const char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}
static int kr_setup_signalfd() {
sigset_t mask;
int sfd;
int flags;
sigemptyset(&mask);
/* sigaddset(&mask, SIGINT);
* sigaddset(&mask, SIGQUIT);
* sigaddset(&mask, SIGTERM);
* If we don't block all the signals for our signalfd,
* we are going to miss some unless we are also using
* and old school signal handler for them..
*/
sigfillset(&mask);
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
handle_error("sigprocmask");
}
flags = SFD_NONBLOCK | SFD_CLOEXEC;
sfd = signalfd(-1, &mask, flags);
if (sfd == 1) {
handle_error("signalfd");
}
return sfd;
}
/*
void set_nonblocking_cloexec(int fd) {
int flags;
flags = fcntl(fd, F_GETFD);
if (flags == -1) {
handle_error("fcntl F_GETFD");
}
flags |= O_CLOEXEC | O_NONBLOCK;
if (fcntl(fd, F_SETFD, flags) == -1) {
handle_error("fcntl F_SETFD");
}
}
*/
static void epoll_add_fd(int epollfd, int fd) {
struct epoll_event ev;
/* edge triggering involves allot more state tracking
* than exists in this demo program
* ev.events = EPOLLIN | EPOLLET;*/
ev.events = EPOLLIN;
ev.data.fd = fd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
handle_error("epoll_ctl");
}
}
static int kr_setup_epollfd() {
int epollfd;
/*
* http://man7.org/linux/man-pages/man2/epoll_create.2.html
* the size argument doesn't do anything anymore
* except for having to be more than 0
* just use epoll_create1()
* epollfd = epoll_create(KR_EPOLL_MAX_EVENTS);
*/
epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd == -1) {
handle_error("epoll_create");
}
/*set_nonblocking_cloexec(epollfd);*/
return epollfd;
}
static int process_signal_event(struct epoll_event *event) {
struct signalfd_siginfo sig_info;
int sig_size = sizeof(struct signalfd_siginfo);
int to_read = sig_size;
ssize_t did_read;
int count = 0;
/* while (1) {
* I just like for (;;) better, to me it just more clearly shows, there is no
* variable here, something must break or return
*/
for (;;) {
did_read = read(event->data.fd, &sig_info, to_read);
if (did_read == -1) {
if (errno != EAGAIN) {
handle_error("signal read");
}
break;
}
if (did_read < to_read) {
to_read -= did_read;
continue; }
to_read = sig_size;
++count;
if (sig_info.ssi_signo == SIGINT) {
printf("Got SIGINT\n");
} else if (sig_info.ssi_signo == SIGTERM) {
printf("Got SIGTERM\n");
} else {
printf("Got unexpected signal\n");
}
}
return count;
}
static void print_elapsed_time(void) {
static struct timespec start;
struct timespec curr;
static int first_call = 1;
int secs, nsecs;
if (first_call) {
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
handle_error("clock_gettime");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
handle_error("clock_gettime");
secs = curr.tv_sec - start.tv_sec;
nsecs = curr.tv_nsec - start.tv_nsec;
if (nsecs < 0) {
secs--;
nsecs += 1000000000;
}
printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}
static void process_timerfd_event(int tfd) {
uint64_t nexp;
int ret;
struct itimerspec timer_time;
nexp = 0;
/* we must read the timerfd, otherwise it, wouldn't be able to know
* we got the event */
ret = read(tfd, &nexp, sizeof(nexp));
if (ret == -1) {
perror("read timerfd");
}
printf("\nWe have had %"PRIu64" timer expiration\n", nexp);
/* http://man7.org/linux/man-pages/man2/timerfd_create.2.html
*
* This will tell us when the timer will expire next, if it
* is armed
*/
ret = timerfd_gettime(tfd, &timer_time);
if (ret == -1) {
perror("timerfd_gettime");
}
/* print in prefered way */
}
int main(int argc, char **argv) {
int epollfd;
struct epoll_event events[KR_EPOLL_MAX_EVENTS];
int sfd;
struct itimerspec new_value;
int tfd;
int flags;
int nfds, n;
int done = 0;
int exp = 0;
int max_exp = 5;
epollfd = kr_setup_epollfd();
sfd = kr_setup_signalfd();
epoll_add_fd(epollfd, sfd);
new_value.it_value.tv_sec = 1;
new_value.it_value.tv_nsec = 0;
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_nsec = 0;
flags = TFD_NONBLOCK | TFD_CLOEXEC;
tfd = timerfd_create(CLOCK_MONOTONIC, flags);
if (tfd == -1) {
handle_error("timerfd_create");
}
if (timerfd_settime(tfd, 0, &new_value, NULL) == -1) {
handle_error("timerfd_settime");
}
epoll_add_fd(epollfd, tfd);
print_elapsed_time();
printf("Starting timer!\n");
while (!done) {
nfds = epoll_wait(epollfd, events, KR_EPOLL_MAX_EVENTS, -1);
if (nfds == -1) {
handle_error("epoll_pwait");
}
printf("Mainloop got %d events!\n", nfds);
for (n = 0; n < nfds; n++) {
if (events[n].data.fd == sfd) {
process_signal_event(&events[n]);
} else if (events[n].data.fd == tfd) {
print_elapsed_time();
process_timerfd_event(tfd);
printf("Interval %d\n", exp);
if (++exp == max_exp) {
done = 1;
printf("Times up! Goodbye.\n");
}
}
}
}
/* lets close things */
if (close(tfd)) {
perror("close tfd");
}
if (close(epollfd)) {
perror("close epollfd");
}
if (close(sfd)) {
perror("close sfd");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment