2007-01-05 12:35:49 +03:00
/*
Unix SMB / CIFS implementation .
main select loop and event handling
Copyright ( C ) Andrew Tridgell 2003 - 2005
2009-02-16 10:52:06 +03:00
Copyright ( C ) Stefan Metzmacher 2005 - 2009
* * NOTE ! The following LGPL license applies to the tevent
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 3 of the License , or ( at your option ) any later version .
This library is distributed in the hope that it will be useful ,
2007-01-05 12:35:49 +03:00
but WITHOUT ANY WARRANTY ; without even the implied warranty of
2009-02-16 10:52:06 +03:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2007-01-05 12:35:49 +03:00
*/
2008-04-25 01:28:30 +04:00
# include "replace.h"
2007-01-05 12:35:49 +03:00
# include "system/filesys.h"
# include "system/select.h"
2008-12-16 21:57:09 +03:00
# include "tevent.h"
# include "tevent_util.h"
# include "tevent_internal.h"
2007-01-05 12:35:49 +03:00
struct select_event_context {
/* a pointer back to the generic event_context */
2008-12-29 22:24:57 +03:00
struct tevent_context * ev ;
2007-01-05 12:35:49 +03:00
/* the maximum file descriptor number in fd_events */
int maxfd ;
/* information for exiting from the event loop */
int exit_code ;
} ;
/*
create a select_event_context structure .
*/
2008-12-29 22:24:57 +03:00
static int select_event_context_init ( struct tevent_context * ev )
2007-01-05 12:35:49 +03:00
{
struct select_event_context * select_ev ;
select_ev = talloc_zero ( ev , struct select_event_context ) ;
if ( ! select_ev ) return - 1 ;
select_ev - > ev = ev ;
ev - > additional_data = select_ev ;
return 0 ;
}
/*
recalculate the maxfd
*/
static void calc_maxfd ( struct select_event_context * select_ev )
{
2008-12-29 22:24:57 +03:00
struct tevent_fd * fde ;
2007-01-05 12:35:49 +03:00
select_ev - > maxfd = 0 ;
2009-01-05 19:36:50 +03:00
for ( fde = select_ev - > ev - > fd_events ; fde ; fde = fde - > next ) {
2007-01-05 12:35:49 +03:00
if ( fde - > fd > select_ev - > maxfd ) {
select_ev - > maxfd = fde - > fd ;
}
}
}
/* to mark the ev->maxfd invalid
* this means we need to recalculate it
*/
# define EVENT_INVALID_MAXFD (-1)
/*
destroy an fd_event
*/
2008-12-29 22:24:57 +03:00
static int select_event_fd_destructor ( struct tevent_fd * fde )
2007-01-05 12:35:49 +03:00
{
2008-12-29 22:24:57 +03:00
struct tevent_context * ev = fde - > event_ctx ;
2009-01-05 19:36:50 +03:00
struct select_event_context * select_ev = NULL ;
2007-01-05 12:35:49 +03:00
2009-01-05 19:36:50 +03:00
if ( ev ) {
select_ev = talloc_get_type ( ev - > additional_data ,
struct select_event_context ) ;
2007-01-05 12:35:49 +03:00
2009-01-05 19:36:50 +03:00
if ( select_ev - > maxfd = = fde - > fd ) {
select_ev - > maxfd = EVENT_INVALID_MAXFD ;
}
2007-05-14 04:57:48 +04:00
}
2009-01-05 19:36:50 +03:00
return tevent_common_fd_destructor ( fde ) ;
2007-01-05 12:35:49 +03:00
}
/*
add a fd based event
return NULL on failure ( memory allocation error )
*/
2008-12-29 22:24:57 +03:00
static struct tevent_fd * select_event_add_fd ( struct tevent_context * ev , TALLOC_CTX * mem_ctx ,
2009-01-02 15:26:32 +03:00
int fd , uint16_t flags ,
tevent_fd_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
2007-01-05 12:35:49 +03:00
{
struct select_event_context * select_ev = talloc_get_type ( ev - > additional_data ,
struct select_event_context ) ;
2008-12-29 22:24:57 +03:00
struct tevent_fd * fde ;
2007-01-05 12:35:49 +03:00
2009-01-05 19:36:50 +03:00
fde = tevent_common_add_fd ( ev , mem_ctx , fd , flags ,
handler , private_data ,
handler_name , location ) ;
2007-01-05 12:35:49 +03:00
if ( ! fde ) return NULL ;
if ( fde - > fd > select_ev - > maxfd ) {
select_ev - > maxfd = fde - > fd ;
}
talloc_set_destructor ( fde , select_event_fd_destructor ) ;
return fde ;
}
/*
event loop handling using select ( )
*/
static int select_event_loop_select ( struct select_event_context * select_ev , struct timeval * tvalp )
{
fd_set r_fds , w_fds ;
2008-12-29 22:24:57 +03:00
struct tevent_fd * fde ;
2007-01-05 12:35:49 +03:00
int selrtn ;
/* we maybe need to recalculate the maxfd */
if ( select_ev - > maxfd = = EVENT_INVALID_MAXFD ) {
calc_maxfd ( select_ev ) ;
}
FD_ZERO ( & r_fds ) ;
FD_ZERO ( & w_fds ) ;
/* setup any fd events */
2009-01-05 19:36:50 +03:00
for ( fde = select_ev - > ev - > fd_events ; fde ; fde = fde - > next ) {
2009-01-02 18:25:29 +03:00
if ( fde - > flags & TEVENT_FD_READ ) {
2007-01-05 12:35:49 +03:00
FD_SET ( fde - > fd , & r_fds ) ;
}
2009-01-02 18:25:29 +03:00
if ( fde - > flags & TEVENT_FD_WRITE ) {
2007-01-05 12:35:49 +03:00
FD_SET ( fde - > fd , & w_fds ) ;
}
}
2009-01-05 18:55:00 +03:00
if ( select_ev - > ev - > signal_events & &
2009-01-02 15:26:32 +03:00
tevent_common_check_signal ( select_ev - > ev ) ) {
2007-01-21 11:23:14 +03:00
return 0 ;
}
2007-01-05 12:35:49 +03:00
selrtn = select ( select_ev - > maxfd + 1 , & r_fds , & w_fds , NULL , tvalp ) ;
2007-01-21 11:23:14 +03:00
if ( selrtn = = - 1 & & errno = = EINTR & &
2009-01-05 18:55:00 +03:00
select_ev - > ev - > signal_events ) {
2009-01-02 15:26:32 +03:00
tevent_common_check_signal ( select_ev - > ev ) ;
2007-01-21 11:23:14 +03:00
return 0 ;
}
2007-01-05 12:35:49 +03:00
if ( selrtn = = - 1 & & errno = = EBADF ) {
/* the socket is dead! this should never
happen as the socket should have first been
made readable and that should have removed
the event , so this must be a bug . This is a
fatal error . */
2009-01-02 15:39:56 +03:00
tevent_debug ( select_ev - > ev , TEVENT_DEBUG_FATAL ,
" ERROR: EBADF on select_event_loop_once \n " ) ;
2007-01-05 12:35:49 +03:00
select_ev - > exit_code = EBADF ;
return - 1 ;
}
if ( selrtn = = 0 & & tvalp ) {
r22661: optimize the handling of directly triggered timed events:
- if someone adds a timed_event with a zero timeval
we now avoid serval gettimeofday() calls and the
event handler doesn't get the current time when it's
called, instead we also pass a zero timeval
- this also makes sure multiple timed events with a zero timeval
are processed in the order there're added.
the little benchmark shows that processing 2000000 directly timed events
is now much faster, while avoiding syscalls at all!
> time ./evtest (with the old code)
real 0m6.388s
user 0m1.740s
sys 0m4.632s
> time ./evtest (with the new code)
real 0m1.498s
user 0m1.496s
sys 0m0.004s
metze@SERNOX:~/devel/samba/4.0/samba4-ci/source> cat evtest.c
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <talloc.h>
#include <events.h>
static void dummy_fde_handler(struct event_context *ev_ctx, struct fd_event *fde,
uint16_t flags, void *private_data)
{
}
static void timeout_handler(struct event_context *ev, struct timed_event *te,
struct timeval tval, void *private_data)
{
uint32_t *countp = (uint32_t *)private_data;
(*countp)++;
if (*countp > 2000000) exit(0);
event_add_timed(ev, ev, tval, timeout_handler, countp);
}
int main(void)
{
struct event_context *ev;
struct timeval tval = { 0, 0 };
uint32_t count = 0;
ev = event_context_init(NULL);
event_add_fd(ev, ev, 0, 0, dummy_fde_handler, NULL);
event_add_timed(ev, ev, tval, timeout_handler, &count);
return event_loop_wait(ev);
}
(This used to be commit 4db64b4ce2320b88d648078cbf86385f6fb44f1f)
2007-05-04 13:22:52 +04:00
/* we don't care about a possible delay here */
2009-01-02 15:26:32 +03:00
tevent_common_loop_timer_delay ( select_ev - > ev ) ;
2007-01-05 12:35:49 +03:00
return 0 ;
}
if ( selrtn > 0 ) {
/* at least one file descriptor is ready - check
which ones and call the handler , being careful to allow
the handler to remove itself when called */
2009-01-05 19:36:50 +03:00
for ( fde = select_ev - > ev - > fd_events ; fde ; fde = fde - > next ) {
2007-01-05 12:35:49 +03:00
uint16_t flags = 0 ;
2009-01-02 18:25:29 +03:00
if ( FD_ISSET ( fde - > fd , & r_fds ) ) flags | = TEVENT_FD_READ ;
if ( FD_ISSET ( fde - > fd , & w_fds ) ) flags | = TEVENT_FD_WRITE ;
2007-01-05 12:35:49 +03:00
if ( flags ) {
fde - > handler ( select_ev - > ev , fde , flags , fde - > private_data ) ;
2009-03-16 14:34:23 +03:00
break ;
2007-01-05 12:35:49 +03:00
}
}
}
return 0 ;
2009-03-16 14:34:23 +03:00
}
2007-01-05 12:35:49 +03:00
/*
do a single event loop using the events defined in ev
*/
2009-03-12 11:33:26 +03:00
static int select_event_loop_once ( struct tevent_context * ev , const char * location )
2007-01-05 12:35:49 +03:00
{
struct select_event_context * select_ev = talloc_get_type ( ev - > additional_data ,
struct select_event_context ) ;
struct timeval tval ;
2009-03-13 17:47:33 +03:00
if ( ev - > signal_events & &
tevent_common_check_signal ( ev ) ) {
return 0 ;
}
if ( ev - > immediate_events & &
tevent_common_loop_immediate ( ev ) ) {
2009-03-16 14:45:48 +03:00
return 0 ;
}
2009-01-02 15:26:32 +03:00
tval = tevent_common_loop_timer_delay ( ev ) ;
2009-02-17 01:34:15 +03:00
if ( tevent_timeval_is_zero ( & tval ) ) {
2007-01-05 12:35:49 +03:00
return 0 ;
}
return select_event_loop_select ( select_ev , & tval ) ;
}
2009-01-02 15:35:32 +03:00
static const struct tevent_ops select_event_ops = {
2009-03-13 17:47:33 +03:00
. context_init = select_event_context_init ,
. add_fd = select_event_add_fd ,
. set_fd_close_fn = tevent_common_fd_set_close_fn ,
. get_fd_flags = tevent_common_fd_get_flags ,
. set_fd_flags = tevent_common_fd_set_flags ,
. add_timer = tevent_common_add_timer ,
. schedule_immediate = tevent_common_schedule_immediate ,
. add_signal = tevent_common_add_signal ,
. loop_once = select_event_loop_once ,
. loop_wait = tevent_common_loop_wait ,
2007-01-05 12:35:49 +03:00
} ;
2009-01-02 15:35:32 +03:00
bool tevent_select_init ( void )
2007-01-05 12:35:49 +03:00
{
2009-01-02 15:35:32 +03:00
return tevent_register_backend ( " select " , & select_event_ops ) ;
2007-01-05 12:35:49 +03:00
}