2 * Server main select() loop
4 * Copyright (C) 1998 Alexandre Julliard
15 #include <sys/types.h>
24 struct timeout_user *next; /* next in sorted timeout list */
25 struct timeout_user *prev; /* prev in sorted timeout list */
26 struct timeval when; /* timeout expiry (absolute time) */
27 timeout_callback callback; /* callback function */
28 void *private; /* callback private data */
31 static struct object **poll_users; /* users array */
32 static struct pollfd *pollfd; /* poll fd array */
33 static int nb_users; /* count of array entries actually in use */
34 static int active_users; /* current number of active users */
35 static int allocated_users; /* count of allocated entries in the array */
36 static struct object **freelist; /* list of free entries in the array */
38 static struct timeout_user *timeout_head; /* sorted timeouts list head */
39 static struct timeout_user *timeout_tail; /* sorted timeouts list tail */
42 /* add a user and return an opaque handle to it, or -1 on failure */
43 int add_select_user( struct object *obj )
48 ret = freelist - poll_users;
49 freelist = (struct object **)poll_users[ret];
53 if (nb_users == allocated_users)
55 struct object **newusers;
56 struct pollfd *newpoll;
57 int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
58 if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
59 if (!(newpoll = realloc( pollfd, new_count * sizeof(*pollfd) )))
64 poll_users = newusers;
66 allocated_users = new_count;
70 pollfd[ret].fd = obj->fd;
71 pollfd[ret].events = 0;
72 pollfd[ret].revents = 0;
73 poll_users[ret] = obj;
79 /* remove an object from the select list and close its fd */
80 void remove_select_user( struct object *obj )
82 int user = obj->select;
83 assert( poll_users[user] == obj );
85 pollfd[user].events = 0;
86 pollfd[user].revents = 0;
87 poll_users[user] = (struct object *)freelist;
88 freelist = &poll_users[user];
95 /* change the fd of an object (the old fd is closed) */
96 void change_select_fd( struct object *obj, int fd )
98 int user = obj->select;
99 assert( poll_users[user] == obj );
100 pollfd[user].fd = fd;
105 /* set the events that select waits for on this fd */
106 void set_select_events( struct object *obj, int events )
108 int user = obj->select;
109 assert( poll_users[user] == obj );
110 if (events == -1) /* stop waiting on this fd completely */
112 pollfd[user].fd = -1;
113 pollfd[user].events = 0;
114 pollfd[user].revents = 0;
116 else if (pollfd[user].fd != -1) pollfd[user].events = events;
119 /* check if events are pending */
120 int check_select_events( int fd, int events )
125 return poll( &pfd, 1, 0 ) > 0;
128 /* add a timeout user */
129 struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
131 struct timeout_user *user;
132 struct timeout_user *pos;
134 if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
136 user->callback = func;
137 user->private = private;
139 /* Now insert it in the linked list */
141 for (pos = timeout_head; pos; pos = pos->next)
142 if (!time_before( &pos->when, when )) break;
144 if (pos) /* insert it before 'pos' */
146 if ((user->prev = pos->prev)) user->prev->next = user;
147 else timeout_head = user;
151 else /* insert it at the tail */
154 if (timeout_tail) timeout_tail->next = user;
155 else timeout_head = user;
156 user->prev = timeout_tail;
162 /* remove a timeout user */
163 void remove_timeout_user( struct timeout_user *user )
165 if (user->next) user->next->prev = user->prev;
166 else timeout_tail = user->prev;
167 if (user->prev) user->prev->next = user->next;
168 else timeout_head = user->next;
172 /* add a timeout in milliseconds to an absolute time */
173 void add_timeout( struct timeval *when, int timeout )
177 long sec = timeout / 1000;
178 if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
180 when->tv_usec -= 1000000;
187 /* handle the next expired timeout */
188 static void handle_timeout(void)
190 struct timeout_user *user = timeout_head;
191 timeout_head = user->next;
192 if (user->next) user->next->prev = user->prev;
193 else timeout_tail = user->prev;
194 user->callback( user->private );
199 static void sighup_handler()
206 /* server main loop */
207 void select_loop(void)
211 struct sigaction action;
213 /* block the signals we use */
214 sigemptyset( &sigset );
215 sigaddset( &sigset, SIGCHLD );
216 sigaddset( &sigset, SIGHUP );
217 sigprocmask( SIG_BLOCK, &sigset, NULL );
219 /* set the handlers */
220 action.sa_mask = sigset;
222 action.sa_handler = sigchld_handler;
223 sigaction( SIGCHLD, &action, NULL );
224 action.sa_handler = sighup_handler;
225 sigaction( SIGHUP, &action, NULL );
233 gettimeofday( &now, NULL );
236 if (!time_before( &now, &timeout_head->when )) handle_timeout();
239 diff = (timeout_head->when.tv_sec - now.tv_sec) * 1000
240 + (timeout_head->when.tv_usec - now.tv_usec) / 1000;
246 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
248 /* Note: we assume that the signal handlers do not manipulate the pollfd array
249 * or the timeout list, otherwise there is a race here.
251 ret = poll( pollfd, nb_users, diff );
253 sigprocmask( SIG_BLOCK, &sigset, NULL );
258 for (i = 0; i < nb_users; i++)
260 if (pollfd[i].revents)
262 poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents );