2007-01-05 12:35:49 +03:00
/*
Unix SMB / CIFS implementation .
common events code for timed events
Copyright ( C ) Andrew Tridgell 2003 - 2006
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"
2009-01-02 14:53:05 +03:00
# include "system/time.h"
2008-12-16 21:57:09 +03:00
# include "tevent.h"
# include "tevent_internal.h"
# include "tevent_util.h"
2007-01-05 12:35:49 +03:00
2008-06-14 19:23:31 +04:00
/**
compare two timeval structures .
Return - 1 if tv1 < tv2
Return 0 if tv1 = = tv2
Return 1 if tv1 > tv2
*/
2009-02-17 01:34:15 +03:00
int tevent_timeval_compare ( const struct timeval * tv1 , const struct timeval * tv2 )
2008-06-14 19:23:31 +04:00
{
if ( tv1 - > tv_sec > tv2 - > tv_sec ) return 1 ;
if ( tv1 - > tv_sec < tv2 - > tv_sec ) return - 1 ;
if ( tv1 - > tv_usec > tv2 - > tv_usec ) return 1 ;
if ( tv1 - > tv_usec < tv2 - > tv_usec ) return - 1 ;
return 0 ;
}
/**
return a zero timeval
*/
2009-02-17 01:34:15 +03:00
struct timeval tevent_timeval_zero ( void )
2008-06-14 19:23:31 +04:00
{
struct timeval tv ;
tv . tv_sec = 0 ;
tv . tv_usec = 0 ;
return tv ;
}
/**
return a timeval for the current time
*/
2009-02-17 01:34:15 +03:00
struct timeval tevent_timeval_current ( void )
2008-06-14 19:23:31 +04:00
{
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
return tv ;
}
/**
return a timeval struct with the given elements
*/
2009-02-17 01:34:15 +03:00
struct timeval tevent_timeval_set ( uint32_t secs , uint32_t usecs )
2008-06-14 19:23:31 +04:00
{
struct timeval tv ;
tv . tv_sec = secs ;
tv . tv_usec = usecs ;
return tv ;
}
/**
return the difference between two timevals as a timeval
if tv1 comes after tv2 , then return a zero timeval
( this is * tv2 - * tv1 )
*/
2009-02-17 01:34:15 +03:00
struct timeval tevent_timeval_until ( const struct timeval * tv1 ,
const struct timeval * tv2 )
2008-06-14 19:23:31 +04:00
{
struct timeval t ;
2009-02-17 01:34:15 +03:00
if ( tevent_timeval_compare ( tv1 , tv2 ) > = 0 ) {
return tevent_timeval_zero ( ) ;
2008-06-14 19:23:31 +04:00
}
t . tv_sec = tv2 - > tv_sec - tv1 - > tv_sec ;
if ( tv1 - > tv_usec > tv2 - > tv_usec ) {
t . tv_sec - - ;
t . tv_usec = 1000000 - ( tv1 - > tv_usec - tv2 - > tv_usec ) ;
} else {
t . tv_usec = tv2 - > tv_usec - tv1 - > tv_usec ;
}
return t ;
}
/**
return true if a timeval is zero
*/
2009-02-17 01:34:15 +03:00
bool tevent_timeval_is_zero ( const struct timeval * tv )
2008-06-14 19:23:31 +04:00
{
return tv - > tv_sec = = 0 & & tv - > tv_usec = = 0 ;
}
2009-02-17 01:34:15 +03:00
struct timeval tevent_timeval_add ( const struct timeval * tv , uint32_t secs ,
uint32_t usecs )
{
struct timeval tv2 = * tv ;
tv2 . tv_sec + = secs ;
tv2 . tv_usec + = usecs ;
tv2 . tv_sec + = tv2 . tv_usec / 1000000 ;
2009-09-25 18:59:04 +04:00
tv2 . tv_usec = tv2 . tv_usec % 1000000 ;
2009-02-17 01:34:15 +03:00
return tv2 ;
}
/**
return a timeval in the future with a specified offset
*/
struct timeval tevent_timeval_current_ofs ( uint32_t secs , uint32_t usecs )
{
struct timeval tv = tevent_timeval_current ( ) ;
return tevent_timeval_add ( & tv , secs , usecs ) ;
}
2007-01-05 12:35:49 +03:00
/*
destroy a timed event
*/
2009-01-02 15:26:32 +03:00
static int tevent_common_timed_destructor ( struct tevent_timer * te )
2007-01-05 12:35:49 +03:00
{
2009-01-05 21:52:47 +03:00
tevent_debug ( te - > event_ctx , TEVENT_DEBUG_TRACE ,
" Destroying timer event %p \" %s \" \n " ,
te , te - > handler_name ) ;
2009-01-05 21:22:22 +03:00
if ( te - > event_ctx ) {
DLIST_REMOVE ( te - > event_ctx - > timer_events , te ) ;
}
2009-01-05 21:52:47 +03:00
2007-01-05 12:35:49 +03:00
return 0 ;
}
2009-01-02 15:26:32 +03:00
static int tevent_common_timed_deny_destructor ( struct tevent_timer * te )
2007-01-05 12:35:49 +03:00
{
return - 1 ;
}
/*
add a timed event
return NULL on failure ( memory allocation error )
*/
2009-01-02 15:26:32 +03:00
struct tevent_timer * tevent_common_add_timer ( struct tevent_context * ev , TALLOC_CTX * mem_ctx ,
struct timeval next_event ,
tevent_timer_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
2007-01-05 12:35:49 +03:00
{
2008-12-29 22:24:57 +03:00
struct tevent_timer * te , * last_te , * cur_te ;
2007-01-05 12:35:49 +03:00
2008-12-29 22:24:57 +03:00
te = talloc ( mem_ctx ? mem_ctx : ev , struct tevent_timer ) ;
2007-01-05 12:35:49 +03:00
if ( te = = NULL ) return NULL ;
te - > event_ctx = ev ;
te - > next_event = next_event ;
te - > handler = handler ;
te - > private_data = private_data ;
2009-01-02 15:26:32 +03:00
te - > handler_name = handler_name ;
te - > location = location ;
2007-01-05 12:35:49 +03:00
te - > additional_data = NULL ;
/* keep the list ordered */
last_te = NULL ;
2008-12-24 15:52:57 +03:00
for ( cur_te = ev - > timer_events ; cur_te ; cur_te = cur_te - > next ) {
2007-01-05 12:35:49 +03:00
/* if the new event comes before the current one break */
2009-02-17 01:34:15 +03:00
if ( tevent_timeval_compare ( & te - > next_event , & cur_te - > next_event ) < 0 ) {
2007-01-05 12:35:49 +03:00
break ;
}
last_te = cur_te ;
}
2008-12-24 15:52:57 +03:00
DLIST_ADD_AFTER ( ev - > timer_events , te , last_te ) ;
2007-01-05 12:35:49 +03:00
2009-01-02 15:26:32 +03:00
talloc_set_destructor ( te , tevent_common_timed_destructor ) ;
2007-01-05 12:35:49 +03:00
2009-01-05 21:52:47 +03:00
tevent_debug ( ev , TEVENT_DEBUG_TRACE ,
" Added timed event \" %s \" : %p \n " ,
handler_name , te ) ;
2007-01-05 12:35:49 +03:00
return te ;
}
/*
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
do a single event loop using the events defined in ev
return the delay untill the next timed event ,
or zero if a timed event was triggered
2007-01-05 12:35:49 +03:00
*/
2009-01-02 15:26:32 +03:00
struct timeval tevent_common_loop_timer_delay ( struct tevent_context * ev )
2007-01-05 12:35:49 +03:00
{
2009-02-17 01:34:15 +03:00
struct timeval current_time = tevent_timeval_zero ( ) ;
2008-12-29 22:24:57 +03:00
struct tevent_timer * te = ev - > timer_events ;
2007-01-05 12:35:49 +03:00
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
if ( ! te ) {
/* have a default tick time of 30 seconds. This guarantees
that code that uses its own timeout checking will be
able to proceeed eventually */
2009-02-17 01:34:15 +03:00
return tevent_timeval_set ( 30 , 0 ) ;
2007-01-05 12:35:49 +03:00
}
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
/*
* work out the right timeout for the next timed event
*
* avoid the syscall to gettimeofday ( ) if the timed event should
* be triggered directly
*
* if there ' s a delay till the next timed event , we ' re done
* with just returning the delay
*/
2009-02-17 01:34:15 +03:00
if ( ! tevent_timeval_is_zero ( & te - > next_event ) ) {
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
struct timeval delay ;
2009-02-17 01:34:15 +03:00
current_time = tevent_timeval_current ( ) ;
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
2009-02-17 01:34:15 +03:00
delay = tevent_timeval_until ( & current_time , & te - > next_event ) ;
if ( ! tevent_timeval_is_zero ( & delay ) ) {
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
return delay ;
}
}
/*
* ok , we have a timed event that we ' ll process . . .
*/
2007-01-05 12:35:49 +03:00
/* deny the handler to free the event */
2009-01-02 15:26:32 +03:00
talloc_set_destructor ( te , tevent_common_timed_deny_destructor ) ;
2007-01-05 12:35:49 +03:00
/* We need to remove the timer from the list before calling the
* handler because in a semi - async inner event loop called from the
* handler we don ' t want to come across this event again - - vl */
2008-12-24 15:52:57 +03:00
DLIST_REMOVE ( ev - > timer_events , te ) ;
2007-01-05 12:35:49 +03:00
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
/*
* If the timed event was registered for a zero current_time ,
* then we pass a zero timeval here too ! To avoid the
* overhead of gettimeofday ( ) calls .
*
* otherwise we pass the current time
*/
te - > handler ( ev , te , current_time , te - > private_data ) ;
2007-01-05 12:35:49 +03:00
/* The destructor isn't necessary anymore, we've already removed the
* event from the list . */
talloc_set_destructor ( te , NULL ) ;
2009-01-05 21:52:47 +03:00
tevent_debug ( te - > event_ctx , TEVENT_DEBUG_TRACE ,
" Ending timer event %p \" %s \" \n " ,
te , te - > handler_name ) ;
2007-01-05 12:35:49 +03:00
talloc_free ( te ) ;
2009-02-17 01:34:15 +03:00
return tevent_timeval_zero ( ) ;
2007-01-05 12:35:49 +03:00
}