Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Server main select() loop |
| 3 | * |
| 4 | * Copyright (C) 1998 Alexandre Julliard |
| 5 | */ |
| 6 | |
| 7 | #include <assert.h> |
| 8 | #include <errno.h> |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 9 | #include <signal.h> |
| 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> |
| 12 | #include <string.h> |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 13 | #include <sys/poll.h> |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 14 | #include <sys/time.h> |
| 15 | #include <sys/types.h> |
| 16 | #include <unistd.h> |
| 17 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 18 | #include "object.h" |
Alexandre Julliard | 0a70783 | 1999-11-08 05:31:47 +0000 | [diff] [blame] | 19 | #include "thread.h" |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 20 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 21 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 22 | struct timeout_user |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 23 | { |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 24 | 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 Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 29 | }; |
| 30 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 31 | static struct object **poll_users; /* users array */ |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 32 | static struct pollfd *pollfd; /* poll fd array */ |
| 33 | static int nb_users; /* count of array entries actually in use */ |
| 34 | static int active_users; /* current number of active users */ |
| 35 | static int allocated_users; /* count of allocated entries in the array */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 36 | static struct object **freelist; /* list of free entries in the array */ |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 37 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 38 | static struct timeout_user *timeout_head; /* sorted timeouts list head */ |
| 39 | static struct timeout_user *timeout_tail; /* sorted timeouts list tail */ |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 40 | |
| 41 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 42 | /* add a user and return an opaque handle to it, or -1 on failure */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 43 | int add_select_user( struct object *obj ) |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 44 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 45 | int ret; |
| 46 | if (freelist) |
| 47 | { |
| 48 | ret = freelist - poll_users; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 49 | freelist = (struct object **)poll_users[ret]; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 50 | } |
| 51 | else |
| 52 | { |
| 53 | if (nb_users == allocated_users) |
| 54 | { |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 55 | struct object **newusers; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 56 | 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 Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 70 | pollfd[ret].fd = obj->fd; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 71 | pollfd[ret].events = 0; |
| 72 | pollfd[ret].revents = 0; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 73 | poll_users[ret] = obj; |
| 74 | obj->select = ret; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 75 | active_users++; |
| 76 | return ret; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 77 | } |
| 78 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 79 | /* remove an object from the select list and close its fd */ |
| 80 | void remove_select_user( struct object *obj ) |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 81 | { |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 82 | int user = obj->select; |
| 83 | assert( poll_users[user] == obj ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 84 | pollfd[user].fd = -1; |
| 85 | pollfd[user].events = 0; |
| 86 | pollfd[user].revents = 0; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 87 | poll_users[user] = (struct object *)freelist; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 88 | freelist = &poll_users[user]; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 89 | close( obj->fd ); |
| 90 | obj->fd = -1; |
| 91 | obj->select = -1; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 92 | active_users--; |
| 93 | } |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 94 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 95 | /* change the fd of an object (the old fd is closed) */ |
| 96 | void change_select_fd( struct object *obj, int fd ) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 97 | { |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 98 | int user = obj->select; |
| 99 | assert( poll_users[user] == obj ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 100 | pollfd[user].fd = fd; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 101 | close( obj->fd ); |
| 102 | obj->fd = fd; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 103 | } |
| 104 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 105 | /* set the events that select waits for on this fd */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 106 | void set_select_events( struct object *obj, int events ) |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 107 | { |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 108 | 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 Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 117 | } |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 118 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 119 | /* check if events are pending */ |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 120 | int check_select_events( int fd, int events ) |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 121 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 122 | struct pollfd pfd; |
| 123 | pfd.fd = fd; |
| 124 | pfd.events = events; |
| 125 | return poll( &pfd, 1, 0 ) > 0; |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | /* add a timeout user */ |
| 129 | struct 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 Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 138 | |
| 139 | /* Now insert it in the linked list */ |
| 140 | |
| 141 | for (pos = timeout_head; pos; pos = pos->next) |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 142 | if (!time_before( &pos->when, when )) break; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 143 | |
| 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 Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 159 | return user; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 160 | } |
| 161 | |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 162 | /* remove a timeout user */ |
| 163 | void remove_timeout_user( struct timeout_user *user ) |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 164 | { |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 165 | 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 Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 170 | } |
| 171 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 172 | /* add a timeout in milliseconds to an absolute time */ |
| 173 | void add_timeout( struct timeval *when, int timeout ) |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 174 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 175 | if (timeout) |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 176 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 177 | 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 Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 184 | } |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 185 | } |
| 186 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 187 | /* handle the next expired timeout */ |
| 188 | static void handle_timeout(void) |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 189 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 190 | struct timeout_user *user = timeout_head; |
| 191 | timeout_head = user->next; |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 192 | if (user->next) user->next->prev = user->prev; |
| 193 | else timeout_tail = user->prev; |
Alexandre Julliard | 88de35c | 1999-05-16 16:57:49 +0000 | [diff] [blame] | 194 | user->callback( user->private ); |
| 195 | free( user ); |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 196 | } |
| 197 | |
Alexandre Julliard | fb65f7d | 1999-06-22 17:27:58 +0000 | [diff] [blame] | 198 | /* SIGHUP handler */ |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 199 | static void sighup_handler() |
Alexandre Julliard | fb65f7d | 1999-06-22 17:27:58 +0000 | [diff] [blame] | 200 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 201 | #ifdef DEBUG_OBJECTS |
| 202 | dump_objects(); |
Alexandre Julliard | fb65f7d | 1999-06-22 17:27:58 +0000 | [diff] [blame] | 203 | #endif |
Alexandre Julliard | 0a70783 | 1999-11-08 05:31:47 +0000 | [diff] [blame] | 204 | } |
| 205 | |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 206 | /* server main loop */ |
| 207 | void select_loop(void) |
| 208 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 209 | int ret; |
| 210 | sigset_t sigset; |
| 211 | struct sigaction action; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 212 | |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 213 | /* 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 Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 228 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 229 | long diff = -1; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 230 | if (timeout_head) |
| 231 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 232 | struct timeval now; |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 233 | gettimeofday( &now, NULL ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 234 | while (timeout_head) |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 235 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 236 | 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 Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 243 | } |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 244 | } |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 245 | |
| 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 Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 256 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 257 | int i; |
| 258 | for (i = 0; i < nb_users; i++) |
Alexandre Julliard | fb65f7d | 1999-06-22 17:27:58 +0000 | [diff] [blame] | 259 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 260 | if (pollfd[i].revents) |
| 261 | { |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 262 | poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 263 | if (!--ret) break; |
| 264 | } |
Alexandre Julliard | fb65f7d | 1999-06-22 17:27:58 +0000 | [diff] [blame] | 265 | } |
Alexandre Julliard | c6e45ed | 1998-12-27 08:35:39 +0000 | [diff] [blame] | 266 | } |
| 267 | } |
| 268 | } |