blob: c46ccd629fc5b26c2b6a501e0b17c2870992cdf9 [file] [log] [blame]
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +00001/*
2 * Server main select() loop
3 *
4 * Copyright (C) 1998 Alexandre Julliard
5 */
6
7#include <assert.h>
8#include <errno.h>
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +00009#include <signal.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000013#include <sys/poll.h>
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000014#include <sys/time.h>
15#include <sys/types.h>
16#include <unistd.h>
17
Alexandre Julliard43c190e1999-05-15 10:48:19 +000018#include "object.h"
Alexandre Julliard0a707831999-11-08 05:31:47 +000019#include "thread.h"
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000020
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000021
Alexandre Julliard88de35c1999-05-16 16:57:49 +000022struct timeout_user
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000023{
Alexandre Julliard88de35c1999-05-16 16:57:49 +000024 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 */
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000029};
30
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000031static struct object **poll_users; /* users array */
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000032static struct pollfd *pollfd; /* poll fd array */
33static int nb_users; /* count of array entries actually in use */
34static int active_users; /* current number of active users */
35static int allocated_users; /* count of allocated entries in the array */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000036static struct object **freelist; /* list of free entries in the array */
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000037
Alexandre Julliard88de35c1999-05-16 16:57:49 +000038static struct timeout_user *timeout_head; /* sorted timeouts list head */
39static struct timeout_user *timeout_tail; /* sorted timeouts list tail */
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000040
41
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000042/* add a user and return an opaque handle to it, or -1 on failure */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000043int add_select_user( struct object *obj )
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000044{
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000045 int ret;
46 if (freelist)
47 {
48 ret = freelist - poll_users;
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000049 freelist = (struct object **)poll_users[ret];
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000050 }
51 else
52 {
53 if (nb_users == allocated_users)
54 {
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000055 struct object **newusers;
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000056 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) )))
60 {
61 free( newusers );
62 return -1;
63 }
64 poll_users = newusers;
65 pollfd = newpoll;
66 allocated_users = new_count;
67 }
68 ret = nb_users++;
69 }
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000070 pollfd[ret].fd = obj->fd;
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000071 pollfd[ret].events = 0;
72 pollfd[ret].revents = 0;
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000073 poll_users[ret] = obj;
74 obj->select = ret;
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000075 active_users++;
76 return ret;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000077}
78
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000079/* remove an object from the select list and close its fd */
80void remove_select_user( struct object *obj )
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000081{
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000082 int user = obj->select;
83 assert( poll_users[user] == obj );
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000084 pollfd[user].fd = -1;
85 pollfd[user].events = 0;
86 pollfd[user].revents = 0;
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000087 poll_users[user] = (struct object *)freelist;
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000088 freelist = &poll_users[user];
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000089 close( obj->fd );
90 obj->fd = -1;
91 obj->select = -1;
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000092 active_users--;
93}
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +000094
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000095/* change the fd of an object (the old fd is closed) */
96void change_select_fd( struct object *obj, int fd )
Alexandre Julliard247b8ae1999-12-13 00:16:44 +000097{
Alexandre Julliard1dca5e22000-01-01 00:56:27 +000098 int user = obj->select;
99 assert( poll_users[user] == obj );
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000100 pollfd[user].fd = fd;
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000101 close( obj->fd );
102 obj->fd = fd;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000103}
104
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000105/* set the events that select waits for on this fd */
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000106void set_select_events( struct object *obj, int events )
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000107{
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000108 int user = obj->select;
109 assert( poll_users[user] == obj );
110 if (events == -1) /* stop waiting on this fd completely */
111 {
112 pollfd[user].fd = -1;
113 pollfd[user].events = 0;
114 pollfd[user].revents = 0;
115 }
116 else if (pollfd[user].fd != -1) pollfd[user].events = events;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000117}
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000118
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000119/* check if events are pending */
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000120int check_select_events( int fd, int events )
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000121{
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000122 struct pollfd pfd;
123 pfd.fd = fd;
124 pfd.events = events;
125 return poll( &pfd, 1, 0 ) > 0;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000126}
127
128/* add a timeout user */
129struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
130{
131 struct timeout_user *user;
132 struct timeout_user *pos;
133
134 if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
135 user->when = *when;
136 user->callback = func;
137 user->private = private;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000138
139 /* Now insert it in the linked list */
140
141 for (pos = timeout_head; pos; pos = pos->next)
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000142 if (!time_before( &pos->when, when )) break;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000143
144 if (pos) /* insert it before 'pos' */
145 {
146 if ((user->prev = pos->prev)) user->prev->next = user;
147 else timeout_head = user;
148 user->next = pos;
149 pos->prev = user;
150 }
151 else /* insert it at the tail */
152 {
153 user->next = NULL;
154 if (timeout_tail) timeout_tail->next = user;
155 else timeout_head = user;
156 user->prev = timeout_tail;
157 timeout_tail = user;
158 }
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000159 return user;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000160}
161
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000162/* remove a timeout user */
163void remove_timeout_user( struct timeout_user *user )
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000164{
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000165 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;
169 free( user );
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000170}
171
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000172/* add a timeout in milliseconds to an absolute time */
173void add_timeout( struct timeval *when, int timeout )
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000174{
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000175 if (timeout)
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000176 {
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000177 long sec = timeout / 1000;
178 if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
179 {
180 when->tv_usec -= 1000000;
181 when->tv_sec++;
182 }
183 when->tv_sec += sec;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000184 }
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000185}
186
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000187/* handle the next expired timeout */
188static void handle_timeout(void)
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000189{
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000190 struct timeout_user *user = timeout_head;
191 timeout_head = user->next;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000192 if (user->next) user->next->prev = user->prev;
193 else timeout_tail = user->prev;
Alexandre Julliard88de35c1999-05-16 16:57:49 +0000194 user->callback( user->private );
195 free( user );
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000196}
197
Alexandre Julliardfb65f7d1999-06-22 17:27:58 +0000198/* SIGHUP handler */
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000199static void sighup_handler()
Alexandre Julliardfb65f7d1999-06-22 17:27:58 +0000200{
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000201#ifdef DEBUG_OBJECTS
202 dump_objects();
Alexandre Julliardfb65f7d1999-06-22 17:27:58 +0000203#endif
Alexandre Julliard0a707831999-11-08 05:31:47 +0000204}
205
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000206/* server main loop */
207void select_loop(void)
208{
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000209 int ret;
210 sigset_t sigset;
211 struct sigaction action;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000212
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000213 /* block the signals we use */
214 sigemptyset( &sigset );
215 sigaddset( &sigset, SIGCHLD );
216 sigaddset( &sigset, SIGHUP );
217 sigprocmask( SIG_BLOCK, &sigset, NULL );
218
219 /* set the handlers */
220 action.sa_mask = sigset;
221 action.sa_flags = 0;
222 action.sa_handler = sigchld_handler;
223 sigaction( SIGCHLD, &action, NULL );
224 action.sa_handler = sighup_handler;
225 sigaction( SIGHUP, &action, NULL );
226
227 while (active_users)
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000228 {
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000229 long diff = -1;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000230 if (timeout_head)
231 {
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000232 struct timeval now;
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000233 gettimeofday( &now, NULL );
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000234 while (timeout_head)
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000235 {
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000236 if (!time_before( &now, &timeout_head->when )) handle_timeout();
237 else
238 {
239 diff = (timeout_head->when.tv_sec - now.tv_sec) * 1000
240 + (timeout_head->when.tv_usec - now.tv_usec) / 1000;
241 break;
242 }
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000243 }
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000244 }
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000245
246 sigprocmask( SIG_UNBLOCK, &sigset, NULL );
247
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.
250 */
251 ret = poll( pollfd, nb_users, diff );
252
253 sigprocmask( SIG_BLOCK, &sigset, NULL );
254
255 if (ret > 0)
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000256 {
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000257 int i;
258 for (i = 0; i < nb_users; i++)
Alexandre Julliardfb65f7d1999-06-22 17:27:58 +0000259 {
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000260 if (pollfd[i].revents)
261 {
Alexandre Julliard1dca5e22000-01-01 00:56:27 +0000262 poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents );
Alexandre Julliard247b8ae1999-12-13 00:16:44 +0000263 if (!--ret) break;
264 }
Alexandre Julliardfb65f7d1999-06-22 17:27:58 +0000265 }
Alexandre Julliardc6e45ed1998-12-27 08:35:39 +0000266 }
267 }
268}