2 * Server main select() loop
4 * Copyright (C) 1998 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <sys/types.h>
38 struct timeout_user *next; /* next in sorted timeout list */
39 struct timeout_user *prev; /* prev in sorted timeout list */
40 struct timeval when; /* timeout expiry (absolute time) */
41 timeout_callback callback; /* callback function */
42 void *private; /* callback private data */
45 static struct object **poll_users; /* users array */
46 static struct pollfd *pollfd; /* poll fd array */
47 static int nb_users; /* count of array entries actually in use */
48 static int active_users; /* current number of active users */
49 static int allocated_users; /* count of allocated entries in the array */
50 static struct object **freelist; /* list of free entries in the array */
52 static struct timeout_user *timeout_head; /* sorted timeouts list head */
53 static struct timeout_user *timeout_tail; /* sorted timeouts list tail */
56 /* add a user and return an opaque handle to it, or -1 on failure */
57 int add_select_user( struct object *obj )
62 ret = freelist - poll_users;
63 freelist = (struct object **)poll_users[ret];
67 if (nb_users == allocated_users)
69 struct object **newusers;
70 struct pollfd *newpoll;
71 int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
72 if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
73 if (!(newpoll = realloc( pollfd, new_count * sizeof(*pollfd) )))
76 poll_users = newusers;
82 poll_users = newusers;
84 allocated_users = new_count;
88 pollfd[ret].fd = obj->fd;
89 pollfd[ret].events = 0;
90 pollfd[ret].revents = 0;
91 poll_users[ret] = obj;
97 /* remove an object from the select list and close its fd */
98 void remove_select_user( struct object *obj )
100 int user = obj->select;
102 assert( poll_users[user] == obj );
103 pollfd[user].fd = -1;
104 pollfd[user].events = 0;
105 pollfd[user].revents = 0;
106 poll_users[user] = (struct object *)freelist;
107 freelist = &poll_users[user];
114 /* change the fd and events of an object */
115 void change_select_fd( struct object *obj, int fd, int events )
117 int user = obj->select;
118 assert( poll_users[user] == obj );
119 pollfd[user].fd = fd;
120 pollfd[user].events = events;
124 /* set the events that select waits for on this fd */
125 void set_select_events( struct object *obj, int events )
127 int user = obj->select;
128 assert( poll_users[user] == obj );
129 if (events == -1) /* stop waiting on this fd completely */
131 pollfd[user].fd = -1;
132 pollfd[user].events = 0;
133 pollfd[user].revents = 0;
135 else if (pollfd[user].fd != -1) pollfd[user].events = events;
138 /* check if events are pending */
139 int check_select_events( int fd, int events )
144 return poll( &pfd, 1, 0 ) > 0;
147 /* add a timeout user */
148 struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
150 struct timeout_user *user;
151 struct timeout_user *pos;
153 if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
155 user->callback = func;
156 user->private = private;
158 /* Now insert it in the linked list */
160 for (pos = timeout_head; pos; pos = pos->next)
161 if (!time_before( &pos->when, when )) break;
163 if (pos) /* insert it before 'pos' */
165 if ((user->prev = pos->prev)) user->prev->next = user;
166 else timeout_head = user;
170 else /* insert it at the tail */
173 if (timeout_tail) timeout_tail->next = user;
174 else timeout_head = user;
175 user->prev = timeout_tail;
181 /* remove a timeout user */
182 void remove_timeout_user( struct timeout_user *user )
184 if (user->next) user->next->prev = user->prev;
185 else timeout_tail = user->prev;
186 if (user->prev) user->prev->next = user->next;
187 else timeout_head = user->next;
191 /* add a timeout in milliseconds to an absolute time */
192 void add_timeout( struct timeval *when, int timeout )
196 long sec = timeout / 1000;
197 if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
199 when->tv_usec -= 1000000;
206 /* handle the next expired timeout */
207 static void handle_timeout(void)
209 struct timeout_user *user = timeout_head;
210 timeout_head = user->next;
211 if (user->next) user->next->prev = user->prev;
212 else timeout_tail = user->prev;
213 user->callback( user->private );
218 static void sighup_handler()
225 /* SIGTERM handler */
226 static void sigterm_handler()
233 static void sigint_handler()
235 kill_all_processes( NULL, 1 );
240 /* server main loop */
241 void select_loop(void)
245 struct sigaction action;
247 /* block the signals we use */
248 sigemptyset( &sigset );
249 sigaddset( &sigset, SIGCHLD );
250 sigaddset( &sigset, SIGHUP );
251 sigaddset( &sigset, SIGINT );
252 sigaddset( &sigset, SIGQUIT );
253 sigaddset( &sigset, SIGTERM );
254 sigprocmask( SIG_BLOCK, &sigset, NULL );
256 /* set the handlers */
257 action.sa_mask = sigset;
259 action.sa_handler = sigchld_handler;
260 sigaction( SIGCHLD, &action, NULL );
261 action.sa_handler = sighup_handler;
262 sigaction( SIGHUP, &action, NULL );
263 action.sa_handler = sigint_handler;
264 sigaction( SIGINT, &action, NULL );
265 action.sa_handler = sigterm_handler;
266 sigaction( SIGQUIT, &action, NULL );
267 sigaction( SIGTERM, &action, NULL );
275 gettimeofday( &now, NULL );
278 if (!time_before( &now, &timeout_head->when )) handle_timeout();
281 diff = (timeout_head->when.tv_sec - now.tv_sec) * 1000
282 + (timeout_head->when.tv_usec - now.tv_usec) / 1000;
286 if (!active_users) break; /* last user removed by a timeout */
289 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
291 /* Note: we assume that the signal handlers do not manipulate the pollfd array
292 * or the timeout list, otherwise there is a race here.
294 ret = poll( pollfd, nb_users, diff );
296 sigprocmask( SIG_BLOCK, &sigset, NULL );
301 for (i = 0; i < nb_users; i++)
303 if (pollfd[i].revents)
305 poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents );