2 * Server main select() loop
4 * Copyright (C) 1998 Alexandre Julliard
15 #include <sys/types.h>
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 */
31 static struct object
**poll_users
; /* users array */
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 */
36 static struct object
**freelist
; /* list of free entries in the array */
38 static struct timeout_user
*timeout_head
; /* sorted timeouts list head */
39 static struct timeout_user
*timeout_tail
; /* sorted timeouts list tail */
42 /* add a user and return an opaque handle to it, or -1 on failure */
43 int add_select_user( struct object
*obj
)
48 ret
= freelist
- poll_users
;
49 freelist
= (struct object
**)poll_users
[ret
];
53 if (nb_users
== allocated_users
)
55 struct object
**newusers
;
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
) )))
62 poll_users
= newusers
;
68 poll_users
= newusers
;
70 allocated_users
= new_count
;
74 pollfd
[ret
].fd
= obj
->fd
;
75 pollfd
[ret
].events
= 0;
76 pollfd
[ret
].revents
= 0;
77 poll_users
[ret
] = obj
;
83 /* remove an object from the select list and close its fd */
84 void remove_select_user( struct object
*obj
)
86 int user
= obj
->select
;
88 assert( poll_users
[user
] == obj
);
90 pollfd
[user
].events
= 0;
91 pollfd
[user
].revents
= 0;
92 poll_users
[user
] = (struct object
*)freelist
;
93 freelist
= &poll_users
[user
];
100 /* change the fd and events of an object */
101 void change_select_fd( struct object
*obj
, int fd
, int events
)
103 int user
= obj
->select
;
104 assert( poll_users
[user
] == obj
);
105 pollfd
[user
].fd
= fd
;
106 pollfd
[user
].events
= events
;
110 /* set the events that select waits for on this fd */
111 void set_select_events( struct object
*obj
, int events
)
113 int user
= obj
->select
;
114 assert( poll_users
[user
] == obj
);
115 if (events
== -1) /* stop waiting on this fd completely */
117 pollfd
[user
].fd
= -1;
118 pollfd
[user
].events
= 0;
119 pollfd
[user
].revents
= 0;
121 else if (pollfd
[user
].fd
!= -1) pollfd
[user
].events
= events
;
124 /* check if events are pending */
125 int check_select_events( int fd
, int events
)
130 return poll( &pfd
, 1, 0 ) > 0;
133 /* add a timeout user */
134 struct timeout_user
*add_timeout_user( struct timeval
*when
, timeout_callback func
, void *private )
136 struct timeout_user
*user
;
137 struct timeout_user
*pos
;
139 if (!(user
= mem_alloc( sizeof(*user
) ))) return NULL
;
141 user
->callback
= func
;
142 user
->private = private;
144 /* Now insert it in the linked list */
146 for (pos
= timeout_head
; pos
; pos
= pos
->next
)
147 if (!time_before( &pos
->when
, when
)) break;
149 if (pos
) /* insert it before 'pos' */
151 if ((user
->prev
= pos
->prev
)) user
->prev
->next
= user
;
152 else timeout_head
= user
;
156 else /* insert it at the tail */
159 if (timeout_tail
) timeout_tail
->next
= user
;
160 else timeout_head
= user
;
161 user
->prev
= timeout_tail
;
167 /* remove a timeout user */
168 void remove_timeout_user( struct timeout_user
*user
)
170 if (user
->next
) user
->next
->prev
= user
->prev
;
171 else timeout_tail
= user
->prev
;
172 if (user
->prev
) user
->prev
->next
= user
->next
;
173 else timeout_head
= user
->next
;
177 /* add a timeout in milliseconds to an absolute time */
178 void add_timeout( struct timeval
*when
, int timeout
)
182 long sec
= timeout
/ 1000;
183 if ((when
->tv_usec
+= (timeout
- 1000*sec
) * 1000) >= 1000000)
185 when
->tv_usec
-= 1000000;
192 /* handle the next expired timeout */
193 static void handle_timeout(void)
195 struct timeout_user
*user
= timeout_head
;
196 timeout_head
= user
->next
;
197 if (user
->next
) user
->next
->prev
= user
->prev
;
198 else timeout_tail
= user
->prev
;
199 user
->callback( user
->private );
204 static void sighup_handler()
211 /* server main loop */
212 void select_loop(void)
216 struct sigaction action
;
218 /* block the signals we use */
219 sigemptyset( &sigset
);
220 sigaddset( &sigset
, SIGCHLD
);
221 sigaddset( &sigset
, SIGHUP
);
222 sigprocmask( SIG_BLOCK
, &sigset
, NULL
);
224 /* set the handlers */
225 action
.sa_mask
= sigset
;
227 action
.sa_handler
= sigchld_handler
;
228 sigaction( SIGCHLD
, &action
, NULL
);
229 action
.sa_handler
= sighup_handler
;
230 sigaction( SIGHUP
, &action
, NULL
);
238 gettimeofday( &now
, NULL
);
241 if (!time_before( &now
, &timeout_head
->when
)) handle_timeout();
244 diff
= (timeout_head
->when
.tv_sec
- now
.tv_sec
) * 1000
245 + (timeout_head
->when
.tv_usec
- now
.tv_usec
) / 1000;
251 sigprocmask( SIG_UNBLOCK
, &sigset
, NULL
);
253 /* Note: we assume that the signal handlers do not manipulate the pollfd array
254 * or the timeout list, otherwise there is a race here.
256 ret
= poll( pollfd
, nb_users
, diff
);
258 sigprocmask( SIG_BLOCK
, &sigset
, NULL
);
263 for (i
= 0; i
< nb_users
; i
++)
265 if (pollfd
[i
].revents
)
267 poll_users
[i
]->ops
->poll_event( poll_users
[i
], pollfd
[i
].revents
);