Как-то понадобилось создать пару тыщ таймеров для обьектов и по срабатыванию очередного таймера совершать некие операции над обьектами, с которыми эти таймеры были ассоциированы. По наводке от JustMad пощупал timerfd интерфейс, ничего так, ниже дан неотшлифованный примерчик, который создает тыщу таймеров в одном потоке и другим потоком их разгребает, почти не загружая при этом проц :
// тест timerfd подсистемы :
//
// - создает следящий тред
// - добавляет 1024 таймера с разными expire временами в fdset
// - запускает и проверяет expire event
// - при timer expire таймер удаляется из fdset
#include <sys/timerfd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <poll.h>
pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct timers_fds_t {
struct pollfd *pfds;
int nfds;
} timers_fds_t;
timers_fds_t timers_fds;
void remove_timer(int fd)
{
int i;
pthread_mutex_lock(&timer_mutex);
for (i = 0; i < timers_fds.nfds; i++) {
if (timers_fds.pfds[i].fd == fd) {
// освобождаем kernel resources
close(timers_fds.pfds[i].fd);
// меняем fd на последний
memcpy(&(timers_fds.pfds[i]),
&(timers_fds.pfds[timers_fds.nfds - 1]), sizeof(struct pollfd));
timers_fds.nfds--;
if (!timers_fds.nfds) {
printf("All timers expired\n");
exit(0);
}
break;
}
}
pthread_mutex_unlock(&timer_mutex);
}
void *timer_thread(void *args)
{
timers_fds_t local_timers_fds;
int res, i;
while(1) {
if (timers_fds.nfds) {
local_timers_fds.nfds = timers_fds.nfds;
local_timers_fds.pfds =
malloc(local_timers_fds.nfds * sizeof(struct pollfd));
if (!local_timers_fds.pfds) {
printf("no memory\n");
exit(-2);
}
memcpy((void *)local_timers_fds.pfds, (void *)timers_fds.pfds,
sizeof(struct pollfd) * local_timers_fds.nfds);
pthread_mutex_unlock(&timer_mutex);
} else {
pthread_mutex_unlock(&timer_mutex);
usleep(10000);
continue;
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_testcancel();
res = poll(local_timers_fds.pfds, local_timers_fds.nfds, 100); // 100 ms timeout
pthread_testcancel();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (res <= 0) {
if (!res) // timeout
goto next;
if ((errno != EAGAIN) && (errno != EINTR)) { // error
printf("poll return %d: %s\n", res, strerror(errno));
}
goto next;
}
for (i = 0; i < local_timers_fds.nfds; i++) {
if (local_timers_fds.pfds[i].revents & POLLIN) { // timer expired
printf("%d: Expired timer_fd %d\n", i,
local_timers_fds.pfds[i].fd);
remove_timer(local_timers_fds.pfds[i].fd);
// delete timer_fd from pfds
} else if (local_timers_fds.pfds[i].revents) {
printf("got %x event for timer_fd %d\n", local_timers_fds.pfds[i].revents,
local_timers_fds.pfds[i].fd);
}
}
next:
free(local_timers_fds.pfds);
}
return 0;
}
void add_timer(int ms)
{
int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
timers_fds_t local_timers_fds;
struct itimerspec new_value;
if (timer_fd < 1) {
printf("timerfd_create: %s\n", strerror(errno));
exit(-1);
}
pthread_mutex_lock(&timer_mutex);
local_timers_fds.nfds = timers_fds.nfds;
local_timers_fds.pfds =
malloc((local_timers_fds.nfds + 1) * sizeof(struct pollfd));
if (!local_timers_fds.pfds) {
printf("no memory\n");
exit(-2);
}
if (local_timers_fds.nfds) // copy old pfds
memcpy((void *)local_timers_fds.pfds, (void *)timers_fds.pfds,
sizeof(struct pollfd) * local_timers_fds.nfds);
// set timer initial expiration
new_value.it_interval.tv_sec = ms / 1000;
new_value.it_interval.tv_nsec = (ms % 1000) * 1000000;
// period after initial expiration
new_value.it_value.tv_sec = new_value.it_interval.tv_sec;
new_value.it_value.tv_nsec = new_value.it_interval.tv_nsec;
if (timerfd_settime(timer_fd, 0, &new_value, 0)) {
printf("timerfd_settime: %s\n", strerror(errno));
exit(-3);
}
// add timer fd
local_timers_fds.pfds[local_timers_fds.nfds].events = POLLPRI | POLLIN |
POLLRDHUP | POLLERR;
local_timers_fds.pfds[local_timers_fds.nfds].fd = timer_fd;
local_timers_fds.pfds[local_timers_fds.nfds].revents = 0;
local_timers_fds.nfds++;
// copy to global
free(timers_fds.pfds);
timers_fds.pfds = local_timers_fds.pfds;
timers_fds.nfds = local_timers_fds.nfds;
printf("Adding %d timer_fd %d with %d s and %d ns (%d ms)\n",
timers_fds.nfds, timer_fd, ms / 1000, (ms % 1000) * 1000000, ms);
pthread_mutex_unlock(&timer_mutex);
}
int main()
{
pthread_t tid;
pthread_attr_t thr_attr;
void *args = 0;
int err, count = 0, tms;
memset((void *)&timers_fds, 0, sizeof(timers_fds_t));
// create timer thread
pthread_attr_init(&thr_attr);
err = pthread_create(&tid, &thr_attr, timer_thread, args);
if (err) {
printf("pthread_create: %s\n", strerror(err));
exit(-1);
}
// add some random timers, 4 seconds max
while (count++ <= 1024) {
while (!(tms = drand48() * 4000));
add_timer(tms);
}
while(1)
sleep(1);
return 0;
}