Fixed a crash in IDirectDrawSurface:BltFast.
[wine.git] / server / select.c
blob540a09b958cdfa470ed835efb7182d50a77d68f6
1 /*
2 * Server main select() loop
4 * Copyright (C) 1998 Alexandre Julliard
5 */
7 #include <assert.h>
8 #include <errno.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <unistd.h>
17 #include "object.h"
18 #include "thread.h"
20 struct timeout_user
22 struct timeout_user *next; /* next in sorted timeout list */
23 struct timeout_user *prev; /* prev in sorted timeout list */
24 struct timeval when; /* timeout expiry (absolute time) */
25 timeout_callback callback; /* callback function */
26 void *private; /* callback private data */
29 static struct select_user *users[FD_SETSIZE]; /* users array */
30 static fd_set read_set, write_set, except_set; /* current select sets */
31 static int nb_users; /* current number of users */
32 static int max_fd; /* max fd in use */
33 static struct timeout_user *timeout_head; /* sorted timeouts list head */
34 static struct timeout_user *timeout_tail; /* sorted timeouts list tail */
37 /* register a user */
38 void register_select_user( struct select_user *user )
40 assert( !users[user->fd] );
42 users[user->fd] = user;
43 if (user->fd > max_fd) max_fd = user->fd;
44 nb_users++;
47 /* remove a user */
48 void unregister_select_user( struct select_user *user )
50 assert( users[user->fd] == user );
52 FD_CLR( user->fd, &read_set );
53 FD_CLR( user->fd, &write_set );
54 FD_CLR( user->fd, &except_set );
55 users[user->fd] = NULL;
56 if (max_fd == user->fd) while (max_fd && !users[max_fd]) max_fd--;
57 nb_users--;
60 /* set the events that select waits for on this fd */
61 void set_select_events( struct select_user *user, int events )
63 assert( users[user->fd] == user );
64 if (events & READ_EVENT) FD_SET( user->fd, &read_set );
65 else FD_CLR( user->fd, &read_set );
66 if (events & WRITE_EVENT) FD_SET( user->fd, &write_set );
67 else FD_CLR( user->fd, &write_set );
68 if (events & EXCEPT_EVENT) FD_SET( user->fd, &except_set );
69 else FD_CLR( user->fd, &except_set );
72 /* check if events are pending */
73 int check_select_events( struct select_user *user, int events )
75 fd_set read_fds, write_fds, except_fds;
76 struct timeval tv = { 0, 0 };
78 FD_ZERO( &read_fds );
79 FD_ZERO( &write_fds );
80 FD_ZERO( &except_fds );
81 if (events & READ_EVENT) FD_SET( user->fd, &read_fds );
82 if (events & WRITE_EVENT) FD_SET( user->fd, &write_fds );
83 if (events & EXCEPT_EVENT) FD_SET( user->fd, &except_fds );
84 return select( user->fd + 1, &read_fds, &write_fds, &except_fds, &tv ) > 0;
87 /* add a timeout user */
88 struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
90 struct timeout_user *user;
91 struct timeout_user *pos;
93 if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
94 user->when = *when;
95 user->callback = func;
96 user->private = private;
98 /* Now insert it in the linked list */
100 for (pos = timeout_head; pos; pos = pos->next)
102 if (pos->when.tv_sec > user->when.tv_sec) break;
103 if ((pos->when.tv_sec == user->when.tv_sec) &&
104 (pos->when.tv_usec > user->when.tv_usec)) break;
107 if (pos) /* insert it before 'pos' */
109 if ((user->prev = pos->prev)) user->prev->next = user;
110 else timeout_head = user;
111 user->next = pos;
112 pos->prev = user;
114 else /* insert it at the tail */
116 user->next = NULL;
117 if (timeout_tail) timeout_tail->next = user;
118 else timeout_head = user;
119 user->prev = timeout_tail;
120 timeout_tail = user;
122 return user;
125 /* remove a timeout user */
126 void remove_timeout_user( struct timeout_user *user )
128 if (user->next) user->next->prev = user->prev;
129 else timeout_tail = user->prev;
130 if (user->prev) user->prev->next = user->next;
131 else timeout_head = user->next;
132 free( user );
135 /* make an absolute timeout value from a relative timeout in milliseconds */
136 void make_timeout( struct timeval *when, int timeout )
138 gettimeofday( when, 0 );
139 if (!timeout) return;
140 if ((when->tv_usec += (timeout % 1000) * 1000) >= 1000000)
142 when->tv_usec -= 1000000;
143 when->tv_sec++;
145 when->tv_sec += timeout / 1000;
148 /* handle an expired timeout */
149 static void handle_timeout( struct timeout_user *user )
151 if (user->next) user->next->prev = user->prev;
152 else timeout_tail = user->prev;
153 if (user->prev) user->prev->next = user->next;
154 else timeout_head = user->next;
155 user->callback( user->private );
156 free( user );
159 #ifdef DEBUG_OBJECTS
160 static int do_dump_objects;
162 /* SIGHUP handler */
163 static void sighup()
165 do_dump_objects = 1;
167 #endif
169 /* dummy SIGCHLD handler */
170 static void sigchld()
174 /* server main loop */
175 void select_loop(void)
177 int i, ret;
179 setsid();
180 signal( SIGPIPE, SIG_IGN );
181 signal( SIGCHLD, sigchld );
182 #ifdef DEBUG_OBJECTS
183 signal( SIGHUP, sighup );
184 #endif
186 while (nb_users)
188 fd_set read = read_set, write = write_set, except = except_set;
189 if (timeout_head)
191 struct timeval tv, now;
192 gettimeofday( &now, NULL );
193 if ((timeout_head->when.tv_sec < now.tv_sec) ||
194 ((timeout_head->when.tv_sec == now.tv_sec) &&
195 (timeout_head->when.tv_usec < now.tv_usec)))
197 handle_timeout( timeout_head );
198 continue;
200 tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec;
201 if ((tv.tv_usec = timeout_head->when.tv_usec - now.tv_usec) < 0)
203 tv.tv_usec += 1000000;
204 tv.tv_sec--;
206 #if 0
207 printf( "select: " );
208 for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
209 (FD_ISSET( i, &write_set ) ? 'w' : '-') );
210 printf( " timeout %d.%06d\n", tv.tv_sec, tv.tv_usec );
211 #endif
212 ret = select( max_fd + 1, &read, &write, &except, &tv );
214 else /* no timeout */
216 #if 0
217 printf( "select: " );
218 for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
219 (FD_ISSET( i, &write_set ) ? 'w' : '-') );
220 printf( " no timeout\n" );
221 #endif
222 ret = select( max_fd + 1, &read, &write, &except, NULL );
225 if (!ret) continue;
226 if (ret == -1) {
227 if (errno == EINTR)
229 #ifdef DEBUG_OBJECTS
230 if (do_dump_objects) dump_objects();
231 do_dump_objects = 0;
232 #endif
233 wait4_thread( NULL, 0 );
234 continue;
236 perror("select");
237 continue;
240 for (i = 0; i <= max_fd; i++)
242 int event = 0;
243 if (FD_ISSET( i, &except )) event |= EXCEPT_EVENT;
244 if (FD_ISSET( i, &write )) event |= WRITE_EVENT;
245 if (FD_ISSET( i, &read )) event |= READ_EVENT;
247 /* Note: users[i] might be NULL here, because an event routine
248 called in an earlier pass of this loop might have removed
249 the current user ... */
250 if (event && users[i])
251 users[i]->func( event, users[i]->private );