2007-01-05 09:35:49 +00:00
/*
Unix SMB / CIFS implementation .
main select loop and event handling
Copyright ( C ) Andrew Tridgell 2003 - 2005
2009-02-16 08:52:06 +01: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 09:35:49 +00:00
but WITHOUT ANY WARRANTY ; without even the implied warranty of
2009-02-16 08:52:06 +01: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 09:35:49 +00:00
*/
2008-04-24 17:28:30 -04:00
# include "replace.h"
2007-01-05 09:35:49 +00:00
# include "system/filesys.h"
# include "system/select.h"
2008-12-16 19:57:09 +01:00
# include "tevent.h"
# include "tevent_util.h"
# include "tevent_internal.h"
2007-01-05 09:35:49 +00:00
struct select_event_context {
/* a pointer back to the generic event_context */
2008-12-29 20:24:57 +01:00
struct tevent_context * ev ;
2007-01-05 09:35:49 +00: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 20:24:57 +01:00
static int select_event_context_init ( struct tevent_context * ev )
2007-01-05 09:35:49 +00:00
{
struct select_event_context * select_ev ;
2013-02-11 10:53:15 -08:00
/*
* We might be called during tevent_re_initialise ( )
* which means we need to free our old additional_data .
*/
TALLOC_FREE ( ev - > additional_data ) ;
2007-01-05 09:35:49 +00:00
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 20:24:57 +01:00
struct tevent_fd * fde ;
2007-01-05 09:35:49 +00:00
select_ev - > maxfd = 0 ;
2009-01-05 17:36:50 +01:00
for ( fde = select_ev - > ev - > fd_events ; fde ; fde = fde - > next ) {
2007-01-05 09:35:49 +00: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 20:24:57 +01:00
static int select_event_fd_destructor ( struct tevent_fd * fde )
2007-01-05 09:35:49 +00:00
{
2008-12-29 20:24:57 +01:00
struct tevent_context * ev = fde - > event_ctx ;
2009-01-05 17:36:50 +01:00
struct select_event_context * select_ev = NULL ;
2007-01-05 09:35:49 +00:00
2009-01-05 17:36:50 +01:00
if ( ev ) {
select_ev = talloc_get_type ( ev - > additional_data ,
struct select_event_context ) ;
2007-01-05 09:35:49 +00:00
2009-01-05 17:36:50 +01:00
if ( select_ev - > maxfd = = fde - > fd ) {
select_ev - > maxfd = EVENT_INVALID_MAXFD ;
}
2007-05-14 00:57:48 +00:00
}
2009-01-05 17:36:50 +01:00
return tevent_common_fd_destructor ( fde ) ;
2007-01-05 09:35:49 +00:00
}
/*
add a fd based event
return NULL on failure ( memory allocation error )
*/
2008-12-29 20:24:57 +01:00
static struct tevent_fd * select_event_add_fd ( struct tevent_context * ev , TALLOC_CTX * mem_ctx ,
2009-01-02 13:26:32 +01:00
int fd , uint16_t flags ,
tevent_fd_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
2007-01-05 09:35:49 +00:00
{
struct select_event_context * select_ev = talloc_get_type ( ev - > additional_data ,
struct select_event_context ) ;
2008-12-29 20:24:57 +01:00
struct tevent_fd * fde ;
2007-01-05 09:35:49 +00:00
2011-02-28 11:23:53 -08:00
if ( fd < 0 | | fd > = FD_SETSIZE ) {
errno = EBADF ;
return NULL ;
}
2009-01-05 17:36:50 +01:00
fde = tevent_common_add_fd ( ev , mem_ctx , fd , flags ,
handler , private_data ,
handler_name , location ) ;
2007-01-05 09:35:49 +00:00
if ( ! fde ) return NULL ;
tevent: Fix maxfd calculation in tevent_select
When doing
fd1 = tevent_add_fd(ev, ev, 2, 0, NULL, NULL);
fd2 = tevent_add_fd(ev, ev, 3, 0, NULL, NULL);
TALLOC_FREE(fd2);
fd2 = tevent_add_fd(ev, ev, 1, 0, NULL, NULL);
we end up with select_ev->maxfd==1. This is wrong.
An alternative fix might be to make select_ev->maxfd an unsigned int and make
EVENT_INVALID_MAXFD==UINT_MAX. But in theory we might end up with an fd of
UINT_MAX.
std_event_add_fd() contains exactly the same piece of code, so I'm directly
pushing it.
Volker
2010-06-10 09:41:11 +02:00
if ( ( select_ev - > maxfd ! = EVENT_INVALID_MAXFD )
& & ( fde - > fd > select_ev - > maxfd ) ) {
2007-01-05 09:35:49 +00:00
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 20:24:57 +01:00
struct tevent_fd * fde ;
2007-01-05 09:35:49 +00:00
int selrtn ;
2013-02-15 11:24:59 +01:00
int select_errno ;
2007-01-05 09:35:49 +00:00
/* 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 17:36:50 +01:00
for ( fde = select_ev - > ev - > fd_events ; fde ; fde = fde - > next ) {
2011-02-28 11:23:53 -08:00
if ( fde - > fd < 0 | | fde - > fd > = FD_SETSIZE ) {
2013-02-28 14:43:55 +01:00
tevent_debug ( select_ev - > ev , TEVENT_DEBUG_FATAL ,
" ERROR: EBADF fd[%d] >= %d "
" select_event_loop_once \n " ,
fde - > fd , FD_SETSIZE ) ;
2011-02-28 11:23:53 -08:00
errno = EBADF ;
return - 1 ;
}
2009-01-02 16:25:29 +01:00
if ( fde - > flags & TEVENT_FD_READ ) {
2007-01-05 09:35:49 +00:00
FD_SET ( fde - > fd , & r_fds ) ;
}
2009-01-02 16:25:29 +01:00
if ( fde - > flags & TEVENT_FD_WRITE ) {
2007-01-05 09:35:49 +00:00
FD_SET ( fde - > fd , & w_fds ) ;
}
}
2009-01-05 16:55:00 +01:00
if ( select_ev - > ev - > signal_events & &
2009-01-02 13:26:32 +01:00
tevent_common_check_signal ( select_ev - > ev ) ) {
2007-01-21 08:23:14 +00:00
return 0 ;
}
2012-06-05 16:00:07 +10:00
tevent_trace_point_callback ( select_ev - > ev , TEVENT_TRACE_BEFORE_WAIT ) ;
2007-01-05 09:35:49 +00:00
selrtn = select ( select_ev - > maxfd + 1 , & r_fds , & w_fds , NULL , tvalp ) ;
2013-02-15 11:24:59 +01:00
select_errno = errno ;
2012-06-05 16:00:07 +10:00
tevent_trace_point_callback ( select_ev - > ev , TEVENT_TRACE_AFTER_WAIT ) ;
2007-01-05 09:35:49 +00:00
2013-02-15 11:24:59 +01:00
if ( selrtn = = - 1 & & select_errno = = EINTR & &
2009-01-05 16:55:00 +01:00
select_ev - > ev - > signal_events ) {
2009-01-02 13:26:32 +01:00
tevent_common_check_signal ( select_ev - > ev ) ;
2007-01-21 08:23:14 +00:00
return 0 ;
}
2013-02-15 11:24:59 +01:00
if ( selrtn = = - 1 & & select_errno = = EBADF ) {
2007-01-05 09:35:49 +00:00
/* 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 13:39:56 +01:00
tevent_debug ( select_ev - > ev , TEVENT_DEBUG_FATAL ,
" ERROR: EBADF on select_event_loop_once \n " ) ;
2007-01-05 09:35:49 +00: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 09:22:52 +00:00
/* we don't care about a possible delay here */
2009-01-02 13:26:32 +01:00
tevent_common_loop_timer_delay ( select_ev - > ev ) ;
2007-01-05 09:35:49 +00: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 17:36:50 +01:00
for ( fde = select_ev - > ev - > fd_events ; fde ; fde = fde - > next ) {
2007-01-05 09:35:49 +00:00
uint16_t flags = 0 ;
2013-02-27 10:18:44 -08:00
if ( FD_ISSET ( fde - > fd , & r_fds ) & & ( fde - > flags & TEVENT_FD_READ ) ) {
flags | = TEVENT_FD_READ ;
}
if ( FD_ISSET ( fde - > fd , & w_fds ) & & ( fde - > flags & TEVENT_FD_WRITE ) ) {
flags | = TEVENT_FD_WRITE ;
}
2007-01-05 09:35:49 +00:00
if ( flags ) {
2013-02-21 15:44:30 +01:00
DLIST_DEMOTE ( select_ev - > ev - > fd_events , fde , struct tevent_fd ) ;
2007-01-05 09:35:49 +00:00
fde - > handler ( select_ev - > ev , fde , flags , fde - > private_data ) ;
2009-03-16 12:34:23 +01:00
break ;
2007-01-05 09:35:49 +00:00
}
}
}
return 0 ;
2009-03-16 12:34:23 +01:00
}
2007-01-05 09:35:49 +00:00
/*
do a single event loop using the events defined in ev
*/
2009-03-12 09:33:26 +01:00
static int select_event_loop_once ( struct tevent_context * ev , const char * location )
2007-01-05 09:35:49 +00:00
{
struct select_event_context * select_ev = talloc_get_type ( ev - > additional_data ,
struct select_event_context ) ;
struct timeval tval ;
2009-03-13 15:47:33 +01:00
if ( ev - > signal_events & &
tevent_common_check_signal ( ev ) ) {
return 0 ;
}
if ( ev - > immediate_events & &
tevent_common_loop_immediate ( ev ) ) {
2009-03-16 12:45:48 +01:00
return 0 ;
}
2009-01-02 13:26:32 +01:00
tval = tevent_common_loop_timer_delay ( ev ) ;
2009-02-16 23:34:15 +01:00
if ( tevent_timeval_is_zero ( & tval ) ) {
2007-01-05 09:35:49 +00:00
return 0 ;
}
return select_event_loop_select ( select_ev , & tval ) ;
}
2009-01-02 13:35:32 +01:00
static const struct tevent_ops select_event_ops = {
2009-03-13 15:47:33 +01: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 09:35:49 +00:00
} ;
2010-04-18 12:47:00 +10:00
_PRIVATE_ bool tevent_select_init ( void )
2007-01-05 09:35:49 +00:00
{
2009-01-02 13:35:32 +01:00
return tevent_register_backend ( " select " , & select_event_ops ) ;
2007-01-05 09:35:49 +00:00
}