#include <stdio.h> #include <stdbool.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/select.h> #include <sys/time.h> #include "runloop.h" #include "util.h" struct fd_list_item{ int fd; runloop_callback *func; i64 last_activity; // -1 if timeout is not enabled }; static struct fd_list_item *fd_list; static size_t fd_list_len,fd_list_cap; static i64 timeout_usecs=0; static runloop_callback *timeout_callback=NULL; static bool sigint_received=false; static void signal_handler(int sig){ if(sig==SIGINT){ sigint_received=true; } } __attribute__((constructor)) static void constructor(void){ fd_list_cap=16; fd_list_len=0; fd_list=malloc(fd_list_cap,struct fd_list_item); signal(SIGINT,signal_handler); } void runloop_set_timeout(i64 usecs,runloop_callback *cb){ timeout_usecs=usecs; timeout_callback=cb; } void runloop_add_fd(int fd,runloop_callback *func,bool use_timeout){ if(fd_list_len==fd_list_cap){ fd_list_cap*=2; fd_list=realloc(fd_list,fd_list_cap,struct fd_list_item); } fd_list[fd_list_len].fd=fd; fd_list[fd_list_len].func=func; fd_list[fd_list_len].last_activity=use_timeout ? make_timestamp() : -1; fd_list_len++; } void runloop_run(void){ while(fd_list_len>0){ if(sigint_received){ printf("runloop: SIGINT received\n"); break; } fd_set inset; FD_ZERO(&inset); int maxfd=-1; i64 oldest_timestamp=-1; int oldest_at=-1; for(size_t i=0;i<fd_list_len;i++){ FD_SET(fd_list[i].fd,&inset); if(fd_list[i].fd>maxfd)maxfd=fd_list[i].fd; if(fd_list[i].last_activity!=-1&& (oldest_timestamp==-1||fd_list[i].last_activity<oldest_timestamp)){ oldest_timestamp=fd_list[i].last_activity; oldest_at=i; } } struct timeval tv={.tv_sec=0,.tv_usec=0}; struct timeval *tv_ptr=NULL; if(timeout_usecs>0&&timeout_callback!=NULL&&oldest_timestamp!=-1){ i64 chosen_timeout=timeout_usecs-(make_timestamp()-oldest_timestamp); if(chosen_timeout<0)chosen_timeout=0; tv.tv_sec=chosen_timeout/1000000; tv.tv_usec=chosen_timeout%1000000; tv_ptr=&tv; } int ret=select(maxfd+1,&inset,NULL,NULL,tv_ptr); if(ret==0){ fd_list[oldest_at].last_activity=make_timestamp(); if(timeout_callback(fd_list[oldest_at].fd)){ memmove(fd_list+oldest_at,fd_list+oldest_at+1, (fd_list_len-oldest_at-1)*sizeof(struct fd_list_item)); fd_list_len--; } continue; } if(ret<0){ if(errno==EINTR)continue; // SIGINT goes here die_perror("select"); } for(size_t i=0;i<fd_list_len;i++){ if(FD_ISSET(fd_list[i].fd,&inset)){ if(fd_list[i].last_activity!=-1){ fd_list[i].last_activity=make_timestamp(); } if(fd_list[i].func(fd_list[i].fd)){ memmove(fd_list+i,fd_list+i+1,(fd_list_len-i-1)*sizeof(struct fd_list_item)); i--; fd_list_len--; } } } } }