2 * Server main select() loop
4 * Copyright (C) 1998 Alexandre Julliard
14 #include <sys/types.h>
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 */
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 */
37 void register_select_user( struct select_user
*user
)
39 assert( !users
[user
->fd
] );
41 users
[user
->fd
] = user
;
42 if (user
->fd
> max_fd
) max_fd
= user
->fd
;
47 void unregister_select_user( struct select_user
*user
)
49 assert( users
[user
->fd
] == user
);
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
--;
58 /* set the events that select waits for on this fd */
59 void set_select_events( struct select_user
*user
, int events
)
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
);
68 /* check if events are pending */
69 int check_select_events( struct select_user
*user
, int events
)
71 fd_set read_fds
, write_fds
;
72 struct timeval tv
= { 0, 0 };
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;
81 /* add a timeout user */
82 struct timeout_user
*add_timeout_user( struct timeval
*when
, timeout_callback func
, void *private )
84 struct timeout_user
*user
;
85 struct timeout_user
*pos
;
87 if (!(user
= mem_alloc( sizeof(*user
) ))) return NULL
;
89 user
->callback
= func
;
90 user
->private = private;
92 /* Now insert it in the linked list */
94 for (pos
= timeout_head
; pos
; pos
= pos
->next
)
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;
101 if (pos
) /* insert it before 'pos' */
103 if ((user
->prev
= pos
->prev
)) user
->prev
->next
= user
;
104 else timeout_head
= user
;
108 else /* insert it at the tail */
111 if (timeout_tail
) timeout_tail
->next
= user
;
112 else timeout_head
= user
;
113 user
->prev
= timeout_tail
;
119 /* remove a timeout user */
120 void remove_timeout_user( struct timeout_user
*user
)
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
;
129 /* make an absolute timeout value from a relative timeout in milliseconds */
130 void make_timeout( struct timeval
*when
, int timeout
)
132 gettimeofday( when
, 0 );
133 if (!timeout
) return;
134 if ((when
->tv_usec
+= (timeout
% 1000) * 1000) >= 1000000)
136 when
->tv_usec
-= 1000000;
139 when
->tv_sec
+= timeout
/ 1000;
142 /* handle an expired timeout */
143 static void handle_timeout( struct timeout_user
*user
)
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 );
153 /* server main loop */
154 void select_loop(void)
159 signal( SIGPIPE
, SIG_IGN
);
163 fd_set read
= read_set
, write
= write_set
;
166 struct timeval tv
, now
;
167 gettimeofday( &now
, NULL
);
168 if ((timeout_head
->when
.tv_sec
< now
.tv_sec
) ||
169 ((timeout_head
->when
.tv_sec
== now
.tv_sec
) &&
170 (timeout_head
->when
.tv_usec
< now
.tv_usec
)))
172 handle_timeout( timeout_head
);
175 tv
.tv_sec
= timeout_head
->when
.tv_sec
- now
.tv_sec
;
176 if ((tv
.tv_usec
= timeout_head
->when
.tv_usec
- now
.tv_usec
) < 0)
178 tv
.tv_usec
+= 1000000;
182 printf( "select: " );
183 for (i
= 0; i
<= max_fd
; i
++) printf( "%c", FD_ISSET( i
, &read_set
) ? 'r' :
184 (FD_ISSET( i
, &write_set
) ? 'w' : '-') );
185 printf( " timeout %d.%06d\n", tv
.tv_sec
, tv
.tv_usec
);
187 ret
= select( max_fd
+ 1, &read
, &write
, NULL
, &tv
);
189 else /* no timeout */
192 printf( "select: " );
193 for (i
= 0; i
<= max_fd
; i
++) printf( "%c", FD_ISSET( i
, &read_set
) ? 'r' :
194 (FD_ISSET( i
, &write_set
) ? 'w' : '-') );
195 printf( " no timeout\n" );
197 ret
= select( max_fd
+ 1, &read
, &write
, NULL
, NULL
);
202 if (errno
== EINTR
) continue;
206 for (i
= 0; i
<= max_fd
; i
++)
209 if (FD_ISSET( i
, &write
)) event
|= WRITE_EVENT
;
210 if (FD_ISSET( i
, &read
)) event
|= READ_EVENT
;
212 /* Note: users[i] might be NULL here, because an event routine
213 called in an earlier pass of this loop might have removed
214 the current user ... */
215 if (event
&& users
[i
])
216 users
[i
]->func( event
, users
[i
]->private );