A few optimizations in the process startup requests now that Winelib
[wine] / server / select.c
1 /*
2  * Server main select() loop
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/poll.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include "object.h"
33 #include "thread.h"
34
35
36 struct timeout_user
37 {
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 */
43 };
44
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 */
51
52 static struct timeout_user *timeout_head;   /* sorted timeouts list head */
53 static struct timeout_user *timeout_tail;   /* sorted timeouts list tail */
54
55
56 /* add a user and return an opaque handle to it, or -1 on failure */
57 int add_select_user( struct object *obj )
58 {
59     int ret;
60     if (freelist)
61     {
62         ret = freelist - poll_users;
63         freelist = (struct object **)poll_users[ret];
64     }
65     else
66     {
67         if (nb_users == allocated_users)
68         {
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) )))
74             {
75                 if (allocated_users)
76                     poll_users = newusers;
77                 else
78                     free( newusers );
79                 obj->select = -1;
80                 return -1;
81             }
82             poll_users = newusers;
83             pollfd = newpoll;
84             allocated_users = new_count;
85         }
86         ret = nb_users++;
87     }
88     pollfd[ret].fd = obj->fd;
89     pollfd[ret].events = 0;
90     pollfd[ret].revents = 0;
91     poll_users[ret] = obj;
92     obj->select = ret;
93     active_users++;
94     return ret;
95 }
96
97 /* remove an object from the select list and close its fd */
98 void remove_select_user( struct object *obj )
99 {
100     int user = obj->select;
101     assert( user >= 0 );
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];
108     close( obj->fd );
109     obj->fd = -1;
110     obj->select = -1;
111     active_users--;
112 }
113
114 /* change the fd and events of an object */
115 void change_select_fd( struct object *obj, int fd, int events )
116 {
117     int user = obj->select;
118     assert( poll_users[user] == obj );
119     pollfd[user].fd = fd;
120     pollfd[user].events = events;
121     obj->fd = fd;
122 }
123
124 /* set the events that select waits for on this fd */
125 void set_select_events( struct object *obj, int events )
126 {
127     int user = obj->select;
128     assert( poll_users[user] == obj );
129     if (events == -1)  /* stop waiting on this fd completely */
130     {
131         pollfd[user].fd = -1;
132         pollfd[user].events = 0;
133         pollfd[user].revents = 0;
134     }
135     else if (pollfd[user].fd != -1) pollfd[user].events = events;
136 }
137
138 /* check if events are pending */
139 int check_select_events( int fd, int events )
140 {
141     struct pollfd pfd;
142     pfd.fd     = fd;
143     pfd.events = events;
144     return poll( &pfd, 1, 0 ) > 0;
145 }
146
147 /* add a timeout user */
148 struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
149 {
150     struct timeout_user *user;
151     struct timeout_user *pos;
152
153     if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
154     user->when     = *when;
155     user->callback = func;
156     user->private  = private;
157
158     /* Now insert it in the linked list */
159
160     for (pos = timeout_head; pos; pos = pos->next)
161         if (!time_before( &pos->when, when )) break;
162
163     if (pos)  /* insert it before 'pos' */
164     {
165         if ((user->prev = pos->prev)) user->prev->next = user;
166         else timeout_head = user;
167         user->next = pos;
168         pos->prev = user;
169     }
170     else  /* insert it at the tail */
171     {
172         user->next = NULL;
173         if (timeout_tail) timeout_tail->next = user;
174         else timeout_head = user;
175         user->prev = timeout_tail;
176         timeout_tail = user;
177     }
178     return user;
179 }
180
181 /* remove a timeout user */
182 void remove_timeout_user( struct timeout_user *user )
183 {
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;
188     free( user );
189 }
190
191 /* add a timeout in milliseconds to an absolute time */
192 void add_timeout( struct timeval *when, int timeout )
193 {
194     if (timeout)
195     {
196         long sec = timeout / 1000;
197         if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
198         {
199             when->tv_usec -= 1000000;
200             when->tv_sec++;
201         }
202         when->tv_sec += sec;
203     }
204 }
205
206 /* handle the next expired timeout */
207 static void handle_timeout(void)
208 {
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 );
214     free( user );
215 }
216
217 /* SIGHUP handler */
218 static void sighup_handler()
219 {
220 #ifdef DEBUG_OBJECTS
221     dump_objects();
222 #endif
223 }
224
225 /* SIGTERM handler */
226 static void sigterm_handler()
227 {
228     close_registry();
229     exit(1);
230 }
231
232 /* server main loop */
233 void select_loop(void)
234 {
235     int ret;
236     sigset_t sigset;
237     struct sigaction action;
238
239     /* block the signals we use */
240     sigemptyset( &sigset );
241     sigaddset( &sigset, SIGCHLD );
242     sigaddset( &sigset, SIGHUP );
243     sigaddset( &sigset, SIGINT );
244     sigaddset( &sigset, SIGQUIT );
245     sigaddset( &sigset, SIGTERM );
246     sigprocmask( SIG_BLOCK, &sigset, NULL );
247
248     /* set the handlers */
249     action.sa_mask = sigset;
250     action.sa_flags = 0;
251     action.sa_handler = sigchld_handler;
252     sigaction( SIGCHLD, &action, NULL );
253     action.sa_handler = sighup_handler;
254     sigaction( SIGHUP, &action, NULL );
255     action.sa_handler = sigterm_handler;
256     sigaction( SIGINT, &action, NULL );
257     sigaction( SIGQUIT, &action, NULL );
258     sigaction( SIGTERM, &action, NULL );
259
260     while (active_users)
261     {
262         long diff = -1;
263         if (timeout_head)
264         {
265             struct timeval now;
266             gettimeofday( &now, NULL );
267             while (timeout_head)
268             {
269                 if (!time_before( &now, &timeout_head->when )) handle_timeout();
270                 else
271                 {
272                     diff = (timeout_head->when.tv_sec - now.tv_sec) * 1000
273                             + (timeout_head->when.tv_usec - now.tv_usec) / 1000;
274                     break;
275                 }
276             }
277             if (!active_users) break;  /* last user removed by a timeout */
278         }
279
280         sigprocmask( SIG_UNBLOCK, &sigset, NULL );
281
282         /* Note: we assume that the signal handlers do not manipulate the pollfd array
283          *       or the timeout list, otherwise there is a race here.
284          */
285         ret = poll( pollfd, nb_users, diff );
286
287         sigprocmask( SIG_BLOCK, &sigset, NULL );
288
289         if (ret > 0)
290         {
291             int i;
292             for (i = 0; i < nb_users; i++)
293             {
294                 if (pollfd[i].revents)
295                 {
296                     poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents );
297                     if (!--ret) break;
298                 }
299             }
300         }
301     }
302 }