aboutsummaryrefslogtreecommitdiff
path: root/runloop.c
blob: 119bf01de43af369841400b57a410bbd05b222d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/time.h>
#include "runloop.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;

__attribute__((constructor))
static void constructor(void){
	fd_list_cap=16;
	fd_list_len=0;
	fd_list=malloc(fd_list_cap,struct fd_list_item);
}


static i64 make_timestamp(void){
	struct timeval tv;
	gettimeofday(&tv,NULL);
	return (i64)tv.tv_sec*1000000+tv.tv_usec;
}

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){
		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;
			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--;
				}
			}
		}
	}
}