Made IShellFolder::fnParseDisplayName return a relative pidl.
[wine] / server / select.c
1 /*
2  * Server main select() loop
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  */
6
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>
16
17 #include "object.h"
18
19 struct timeout_user
20 {
21     struct timeout_user  *next;       /* next in sorted timeout list */
22     struct timeout_user  *prev;       /* prev in sorted timeout list */
23     struct timeval        when;       /* timeout expiry (absolute time) */
24     timeout_callback      callback;   /* callback function */
25     void                 *private;    /* callback private data */
26 };
27
28 static struct select_user *users[FD_SETSIZE]; /* users array */
29 static fd_set read_set, write_set;          /* current select sets */
30 static int nb_users;                        /* current number of users */
31 static int max_fd;                          /* max fd in use */
32 static struct timeout_user *timeout_head;   /* sorted timeouts list head */
33 static struct timeout_user *timeout_tail;   /* sorted timeouts list tail */
34
35
36 /* register a user */
37 void register_select_user( struct select_user *user )
38 {
39     assert( !users[user->fd] );
40
41     users[user->fd] = user;
42     if (user->fd > max_fd) max_fd = user->fd;
43     nb_users++;
44 }
45
46 /* remove a user */
47 void unregister_select_user( struct select_user *user )
48 {
49     assert( users[user->fd] == user );
50
51     FD_CLR( user->fd, &read_set );
52     FD_CLR( user->fd, &write_set );
53     users[user->fd] = NULL;
54     if (max_fd == user->fd) while (max_fd && !users[max_fd]) max_fd--;
55     nb_users--;
56 }
57
58 /* set the events that select waits for on this fd */
59 void set_select_events( struct select_user *user, int events )
60 {
61     assert( users[user->fd] == user );
62     if (events & READ_EVENT) FD_SET( user->fd, &read_set );
63     else FD_CLR( user->fd, &read_set );
64     if (events & WRITE_EVENT) FD_SET( user->fd, &write_set );
65     else FD_CLR( user->fd, &write_set );
66 }
67
68 /* check if events are pending */
69 int check_select_events( struct select_user *user, int events )
70 {
71     fd_set read_fds, write_fds;
72     struct timeval tv = { 0, 0 };
73
74     FD_ZERO( &read_fds );
75     FD_ZERO( &write_fds );
76     if (events & READ_EVENT) FD_SET( user->fd, &read_fds );
77     if (events & WRITE_EVENT) FD_SET( user->fd, &write_fds );
78     return select( user->fd + 1, &read_fds, &write_fds, NULL, &tv ) > 0;
79 }
80
81 /* add a timeout user */
82 struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
83 {
84     struct timeout_user *user;
85     struct timeout_user *pos;
86
87     if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
88     user->when     = *when;
89     user->callback = func;
90     user->private  = private;
91
92     /* Now insert it in the linked list */
93
94     for (pos = timeout_head; pos; pos = pos->next)
95     {
96         if (pos->when.tv_sec > user->when.tv_sec) break;
97         if ((pos->when.tv_sec == user->when.tv_sec) &&
98             (pos->when.tv_usec > user->when.tv_usec)) break;
99     }
100
101     if (pos)  /* insert it before 'pos' */
102     {
103         if ((user->prev = pos->prev)) user->prev->next = user;
104         else timeout_head = user;
105         user->next = pos;
106         pos->prev = user;
107     }
108     else  /* insert it at the tail */
109     {
110         user->next = NULL;
111         if (timeout_tail) timeout_tail->next = user;
112         else timeout_head = user;
113         user->prev = timeout_tail;
114         timeout_tail = user;
115     }
116     return user;
117 }
118
119 /* remove a timeout user */
120 void remove_timeout_user( struct timeout_user *user )
121 {
122     if (user->next) user->next->prev = user->prev;
123     else timeout_tail = user->prev;
124     if (user->prev) user->prev->next = user->next;
125     else timeout_head = user->next;
126     free( user );
127 }
128
129 /* make an absolute timeout value from a relative timeout in milliseconds */
130 void make_timeout( struct timeval *when, int timeout )
131 {
132     gettimeofday( when, 0 );
133     if (!timeout) return;
134     if ((when->tv_usec += (timeout % 1000) * 1000) >= 1000000)
135     {
136         when->tv_usec -= 1000000;
137         when->tv_sec++;
138     }
139     when->tv_sec += timeout / 1000;
140 }
141
142 /* handle an expired timeout */
143 static void handle_timeout( struct timeout_user *user )
144 {
145     if (user->next) user->next->prev = user->prev;
146     else timeout_tail = user->prev;
147     if (user->prev) user->prev->next = user->next;
148     else timeout_head = user->next;
149     user->callback( user->private );
150     free( user );
151 }
152
153 #ifdef DEBUG_OBJECTS
154 static int do_dump_objects;
155
156 /* SIGHUP handler */
157 static void sighup()
158 {
159     do_dump_objects = 1;
160 }
161 #endif
162
163 /* server main loop */
164 void select_loop(void)
165 {
166     int i, ret;
167
168     setsid();
169     signal( SIGPIPE, SIG_IGN );
170 #ifdef DEBUG_OBJECTS
171     signal( SIGHUP, sighup );
172 #endif
173
174     while (nb_users)
175     {
176         fd_set read = read_set, write = write_set;
177         if (timeout_head)
178         {
179             struct timeval tv, now;
180             gettimeofday( &now, NULL );
181             if ((timeout_head->when.tv_sec < now.tv_sec) ||
182                 ((timeout_head->when.tv_sec == now.tv_sec) &&
183                  (timeout_head->when.tv_usec < now.tv_usec)))
184             {
185                 handle_timeout( timeout_head );
186                 continue;
187             }
188             tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec;
189             if ((tv.tv_usec = timeout_head->when.tv_usec - now.tv_usec) < 0)
190             {
191                 tv.tv_usec += 1000000;
192                 tv.tv_sec--;
193             }
194 #if 0
195             printf( "select: " );
196             for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
197                                                   (FD_ISSET( i, &write_set ) ? 'w' : '-') );
198             printf( " timeout %d.%06d\n", tv.tv_sec, tv.tv_usec );
199 #endif
200             ret = select( max_fd + 1, &read, &write, NULL, &tv );
201         }
202         else  /* no timeout */
203         {
204 #if 0
205             printf( "select: " );
206             for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
207                                                   (FD_ISSET( i, &write_set ) ? 'w' : '-') );
208             printf( " no timeout\n" );
209 #endif
210             ret = select( max_fd + 1, &read, &write, NULL, NULL );
211         }
212
213         if (!ret) continue;
214         if (ret == -1) {
215             if (errno == EINTR)
216             {
217 #ifdef DEBUG_OBJECTS
218                 if (do_dump_objects) dump_objects();
219                 do_dump_objects = 0;
220 #endif
221                 continue;
222             }
223             perror("select");
224         }
225
226         for (i = 0; i <= max_fd; i++)
227         {
228             int event = 0;
229             if (FD_ISSET( i, &write )) event |= WRITE_EVENT;
230             if (FD_ISSET( i, &read ))  event |= READ_EVENT;
231
232             /* Note: users[i] might be NULL here, because an event routine
233                called in an earlier pass of this loop might have removed 
234                the current user ... */
235             if (event && users[i]) 
236                 users[i]->func( event, users[i]->private );
237         }
238     }
239 }