Possible crash in select_loop() fixed.
[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 <fcntl.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "server/object.h"
19
20 /* select user fd */
21 struct user
22 {
23     struct timeval           when;    /* timeout expiry (absolute time) */
24     struct user             *next;    /* next in sorted timeout list */
25     struct user             *prev;    /* prev in sorted timeout list */
26     const struct select_ops *ops;   /* user operations list */
27     int                      fd;      /* user fd */
28     void                    *private; /* user private data */
29 };
30
31 static struct user *users[FD_SETSIZE];      /* users array */
32 static fd_set read_set, write_set;          /* current select sets */
33 static int nb_users;                        /* current number of users */
34 static int max_fd;                          /* max fd in use */
35 static struct user *timeout_head;           /* sorted timeouts list head */
36 static struct user *timeout_tail;           /* sorted timeouts list tail */
37
38
39 /* add a user */
40 int add_select_user( int fd, int events, const struct select_ops *ops, void *private )
41 {
42     int flags;
43     struct user *user = malloc( sizeof(*user) );
44     if (!user) return -1;
45     assert( !users[fd] );
46
47     user->ops          = ops;
48     user->when.tv_sec  = 0;
49     user->when.tv_usec = 0;
50     user->fd           = fd;
51     user->private      = private;
52
53     flags = fcntl( fd, F_GETFL, 0 );
54     fcntl( fd, F_SETFL, flags | O_NONBLOCK );
55
56     users[fd] = user;
57     set_select_events( fd, events );
58     if (fd > max_fd) max_fd = fd;
59     nb_users++;
60     return fd;
61 }
62
63 /* remove a user */
64 void remove_select_user( int fd )
65 {
66     struct user *user = users[fd];
67     assert( user );
68
69     set_select_timeout( fd, 0 );
70     set_select_events( fd, 0 );
71     users[fd] = NULL;
72     if (max_fd == fd) while (max_fd && !users[max_fd]) max_fd--;
73     nb_users--;
74     free( user );
75 }
76
77 /* set a user timeout */
78 void set_select_timeout( int fd, struct timeval *when )
79 {
80     struct user *user = users[fd];
81     struct user *pos;
82     assert( user );
83
84     if (user->when.tv_sec || user->when.tv_usec)
85     {
86         /* there is already a timeout */
87         if (user->next) user->next->prev = user->prev;
88         else timeout_tail = user->prev;
89         if (user->prev) user->prev->next = user->next;
90         else timeout_head = user->next;
91         user->when.tv_sec = user->when.tv_usec = 0;
92     }
93     if (!when) return;  /* no timeout */
94     user->when = *when;
95
96     /* Now insert it in the linked list */
97
98     for (pos = timeout_head; pos; pos = pos->next)
99     {
100         if (pos->when.tv_sec > user->when.tv_sec) break;
101         if ((pos->when.tv_sec == user->when.tv_sec) &&
102             (pos->when.tv_usec > user->when.tv_usec)) break;
103     }
104
105     if (pos)  /* insert it before 'pos' */
106     {
107         if ((user->prev = pos->prev)) user->prev->next = user;
108         else timeout_head = user;
109         user->next = pos;
110         pos->prev = user;
111     }
112     else  /* insert it at the tail */
113     {
114         user->next = NULL;
115         if (timeout_tail) timeout_tail->next = user;
116         else timeout_head = user;
117         user->prev = timeout_tail;
118         timeout_tail = user;
119     }
120 }
121
122 /* set the events that select waits for on this fd */
123 void set_select_events( int fd, int events )
124 {
125     if (events & READ_EVENT) FD_SET( fd, &read_set );
126     else FD_CLR( fd, &read_set );
127     if (events & WRITE_EVENT) FD_SET( fd, &write_set );
128     else FD_CLR( fd, &write_set );
129 }
130
131 /* get a user private data, checking the type */
132 void *get_select_private_data( const struct select_ops *ops, int fd )
133 {
134     struct user *user = users[fd];
135     assert( user );
136     assert( user->ops == ops );
137     return user->private;
138 }
139
140 /* server main loop */
141 void select_loop(void)
142 {
143     int i, ret;
144
145     setsid();
146     signal( SIGPIPE, SIG_IGN );
147
148     while (nb_users)
149     {
150         fd_set read = read_set, write = write_set;
151 #if 0
152         printf( "select: " );
153         for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
154                                                     (FD_ISSET( i, &write_set ) ? 'w' : '-') );
155         printf( "\n" );
156 #endif
157         if (timeout_head)
158         {
159             struct timeval tv, now;
160             gettimeofday( &now, NULL );
161             if ((timeout_head->when.tv_sec < now.tv_sec) ||
162                 ((timeout_head->when.tv_sec == now.tv_sec) &&
163                  (timeout_head->when.tv_usec < now.tv_usec)))
164             {
165                 timeout_head->ops->timeout( timeout_head->fd, timeout_head->private );
166                 continue;
167             }
168             tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec;
169             if ((tv.tv_usec = timeout_head->when.tv_usec - now.tv_usec) < 0)
170             {
171                 tv.tv_usec += 1000000;
172                 tv.tv_sec--;
173             }
174             ret = select( max_fd + 1, &read, &write, NULL, &tv );
175         }
176         else  /* no timeout */
177         {
178             ret = select( max_fd + 1, &read, &write, NULL, NULL );
179         }
180
181         if (!ret) continue;
182         if (ret == -1) perror("select");
183
184         for (i = 0; i <= max_fd; i++)
185         {
186             int event = 0;
187             if (FD_ISSET( i, &write )) event |= WRITE_EVENT;
188             if (FD_ISSET( i, &read ))  event |= READ_EVENT;
189
190             /* Note: users[i] might be NULL here, because an event routine
191                called in an earlier pass of this loop might have removed 
192                the current user ... */
193             if (event && users[i]) 
194                 users[i]->ops->event( i, event, users[i]->private );
195         }
196     }
197 }