This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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