2 * Server main select() loop
4 * Copyright (C) 1998 Alexandre Julliard
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.
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.
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
29 #include <sys/types.h>
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 */
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 */
52 static struct timeout_user
*timeout_head
; /* sorted timeouts list head */
53 static struct timeout_user
*timeout_tail
; /* sorted timeouts list tail */
56 /* add a user and return an opaque handle to it, or -1 on failure */
57 int add_select_user( struct object
*obj
)
62 ret
= freelist
- poll_users
;
63 freelist
= (struct object
**)poll_users
[ret
];
67 if (nb_users
== allocated_users
)
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
) )))
76 poll_users
= newusers
;
82 poll_users
= newusers
;
84 allocated_users
= new_count
;
88 pollfd
[ret
].fd
= obj
->fd
;
89 pollfd
[ret
].events
= 0;
90 pollfd
[ret
].revents
= 0;
91 poll_users
[ret
] = obj
;
97 /* remove an object from the select list and close its fd */
98 void remove_select_user( struct object
*obj
)
100 int user
= obj
->select
;
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
];
114 /* change the fd and events of an object */
115 void change_select_fd( struct object
*obj
, int fd
, int events
)
117 int user
= obj
->select
;
118 assert( poll_users
[user
] == obj
);
119 pollfd
[user
].fd
= fd
;
120 pollfd
[user
].events
= events
;
124 /* set the events that select waits for on this fd */
125 void set_select_events( struct object
*obj
, int events
)
127 int user
= obj
->select
;
128 assert( poll_users
[user
] == obj
);
129 if (events
== -1) /* stop waiting on this fd completely */
131 pollfd
[user
].fd
= -1;
132 pollfd
[user
].events
= 0;
133 pollfd
[user
].revents
= 0;
135 else if (pollfd
[user
].fd
!= -1) pollfd
[user
].events
= events
;
138 /* check if events are pending */
139 int check_select_events( int fd
, int events
)
144 return poll( &pfd
, 1, 0 ) > 0;
147 /* add a timeout user */
148 struct timeout_user
*add_timeout_user( struct timeval
*when
, timeout_callback func
, void *private )
150 struct timeout_user
*user
;
151 struct timeout_user
*pos
;
153 if (!(user
= mem_alloc( sizeof(*user
) ))) return NULL
;
155 user
->callback
= func
;
156 user
->private = private;
158 /* Now insert it in the linked list */
160 for (pos
= timeout_head
; pos
; pos
= pos
->next
)
161 if (!time_before( &pos
->when
, when
)) break;
163 if (pos
) /* insert it before 'pos' */
165 if ((user
->prev
= pos
->prev
)) user
->prev
->next
= user
;
166 else timeout_head
= user
;
170 else /* insert it at the tail */
173 if (timeout_tail
) timeout_tail
->next
= user
;
174 else timeout_head
= user
;
175 user
->prev
= timeout_tail
;
181 /* remove a timeout user */
182 void remove_timeout_user( struct timeout_user
*user
)
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
;
191 /* add a timeout in milliseconds to an absolute time */
192 void add_timeout( struct timeval
*when
, int timeout
)
196 long sec
= timeout
/ 1000;
197 if ((when
->tv_usec
+= (timeout
- 1000*sec
) * 1000) >= 1000000)
199 when
->tv_usec
-= 1000000;
206 /* handle the next expired timeout */
207 static void handle_timeout(void)
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 );
218 static void sighup_handler()
225 /* SIGTERM handler */
226 static void sigterm_handler()
233 static void sigint_handler()
235 kill_all_processes( NULL
, 1 );
240 /* server main loop */
241 void select_loop(void)
245 struct sigaction action
;
247 /* block the signals we use */
248 sigemptyset( &sigset
);
249 sigaddset( &sigset
, SIGCHLD
);
250 sigaddset( &sigset
, SIGHUP
);
251 sigaddset( &sigset
, SIGINT
);
252 sigaddset( &sigset
, SIGQUIT
);
253 sigaddset( &sigset
, SIGTERM
);
254 sigprocmask( SIG_BLOCK
, &sigset
, NULL
);
256 /* set the handlers */
257 action
.sa_mask
= sigset
;
259 action
.sa_handler
= sigchld_handler
;
260 sigaction( SIGCHLD
, &action
, NULL
);
261 action
.sa_handler
= sighup_handler
;
262 sigaction( SIGHUP
, &action
, NULL
);
263 action
.sa_handler
= sigint_handler
;
264 sigaction( SIGINT
, &action
, NULL
);
265 action
.sa_handler
= sigterm_handler
;
266 sigaction( SIGQUIT
, &action
, NULL
);
267 sigaction( SIGTERM
, &action
, NULL
);
275 gettimeofday( &now
, NULL
);
278 if (!time_before( &now
, &timeout_head
->when
)) handle_timeout();
281 diff
= (timeout_head
->when
.tv_sec
- now
.tv_sec
) * 1000
282 + (timeout_head
->when
.tv_usec
- now
.tv_usec
) / 1000;
286 if (!active_users
) break; /* last user removed by a timeout */
289 sigprocmask( SIG_UNBLOCK
, &sigset
, NULL
);
291 /* Note: we assume that the signal handlers do not manipulate the pollfd array
292 * or the timeout list, otherwise there is a race here.
294 ret
= poll( pollfd
, nb_users
, diff
);
296 sigprocmask( SIG_BLOCK
, &sigset
, NULL
);
301 for (i
= 0; i
< nb_users
; i
++)
303 if (pollfd
[i
].revents
)
305 poll_users
[i
]->ops
->poll_event( poll_users
[i
], pollfd
[i
].revents
);