further work on faster loop

pull/5/merge
David Schweikert 13 years ago
parent 57650049fb
commit fdae03d016

@ -11,20 +11,21 @@
* It is highly recommended that your editor is set to this * It is highly recommended that your editor is set to this
* tab stop setting for viewing and editing. * tab stop setting for viewing and editing.
* *
* fping website: http://www.fping.com * fping website: http://www.fping.org
* *
* *
* *
* Current maintainers of fping: * Current maintainers of fping:
* *
* David Papp * David Schweikert
* Suggestions and patches, please email david@remote.net * Please send suggestions and patches to: david@schweikert.ch
* *
* *
* *
* Original author: Roland Schemers <schemers@stanford.edu> * Original author: Roland Schemers <schemers@stanford.edu>
* IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl> * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
* Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de) * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
* Improved main loop: David Schweikert <david@schweikert.ch>
* *
* *
* RCS header information no longer used. It has been moved to the * RCS header information no longer used. It has been moved to the
@ -230,12 +231,17 @@ char *icmp_unreach_str[16] =
/* entry used to keep track of each host we are pinging */ /* entry used to keep track of each host we are pinging */
#define EV_TYPE_PING 1
#define EV_TYPE_TIMEOUT 2
typedef struct host_entry typedef struct host_entry
{ {
struct host_entry *prev,*next; /* double linked list */ /* Each host can have an event attached: either the next time that a ping needs
struct host_entry *sq_prev; /* double linked list for the send-queue */ * to be sent, or the timeout, if the last ping was sent */
struct host_entry *sq_next; /* double linked list for the send-queue */ struct host_entry *ev_prev; /* double linked list for the event-queue */
struct timeval sq_send_time; /* can't send before this time */ struct host_entry *ev_next; /* double linked list for the event-queue */
struct timeval ev_time; /* time, after which this event should happen */
int ev_type; /* event type */
int i; /* index into array */ int i; /* index into array */
char *name; /* name as given by user */ char *name; /* name as given by user */
@ -266,14 +272,12 @@ typedef struct host_entry
HOST_ENTRY *rrlist = NULL; /* linked list of hosts be pinged */ HOST_ENTRY *rrlist = NULL; /* linked list of hosts be pinged */
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */ HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
HOST_ENTRY *cursor;
/* send queue (sq): hosts in this list are ready for a new ping to be sent */ /* event queue (ev): This, together with the ev_next / ev_prev elements are used
/* the inter-ping interval must be respected when going through this list */ * to track the next event happening for each host. This can be either a new ping
/* and sending the pings. However, the per-host interval (-p), doesn't need */ * that needs to be sent or a timeout */
/* to be considered (the host is not here, if that interval didn't pass */ HOST_ENTRY *ev_first;
HOST_ENTRY *sq_first; HOST_ENTRY *ev_last;
HOST_ENTRY *sq_last;
char *prog; char *prog;
int ident; /* our pid */ int ident; /* our pid */
@ -358,6 +362,7 @@ long timeval_diff();
void print_per_system_stats(); void print_per_system_stats();
void print_per_system_splits(); void print_per_system_splits();
void print_global_stats(); void print_global_stats();
void main_loop();
void finish(); void finish();
int handle_random_icmp(); int handle_random_icmp();
char *sprint_tm(); char *sprint_tm();
@ -387,12 +392,13 @@ int wait_for_reply( u_int );
void print_per_system_stats( void ); void print_per_system_stats( void );
void print_per_system_splits( void ); void print_per_system_splits( void );
void print_global_stats( void ); void print_global_stats( void );
void main_loop();
void finish(); void finish();
int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr ); int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr );
char *sprint_tm( int t ); char *sprint_tm( int t );
void sq_enqueue(HOST_ENTRY *h); void ev_enqueue(HOST_ENTRY *h);
HOST_ENTRY *sq_dequeue(); HOST_ENTRY *ev_dequeue();
void sq_remove(HOST_ENTRY *h); void ev_remove(HOST_ENTRY *h);
#endif /* _NO_PROTO */ #endif /* _NO_PROTO */
@ -423,7 +429,6 @@ int main( int argc, char **argv )
#ifdef IPV6 #ifdef IPV6
int opton = 1; int opton = 1;
#endif #endif
u_int lt, ht;
struct protoent *proto; struct protoent *proto;
char *buf; char *buf;
uid_t uid; uid_t uid;
@ -432,6 +437,8 @@ int main( int argc, char **argv )
#else #else
struct sockaddr_in6 sa; struct sockaddr_in6 sa;
#endif #endif
HOST_ENTRY *cursor;
/* check if we are root */ /* check if we are root */
if( geteuid() ) if( geteuid() )
@ -1064,7 +1071,7 @@ int main( int argc, char **argv )
if( !table ) if( !table )
crash_and_burn( "Can't malloc array of hosts" ); crash_and_burn( "Can't malloc array of hosts" );
cursor = rrlist; cursor = ev_first;
for( num_jobs = 0; num_jobs < num_hosts; num_jobs++ ) for( num_jobs = 0; num_jobs < num_hosts; num_jobs++ )
{ {
@ -1087,7 +1094,7 @@ int main( int argc, char **argv )
}/* IF */ }/* IF */
cursor=cursor->next; cursor=cursor->ev_next;
}/* FOR */ }/* FOR */
@ -1108,125 +1115,128 @@ int main( int argc, char **argv )
srandom( start_time.tv_usec ); srandom( start_time.tv_usec );
#endif /* DEBUG || _DEBUG */ #endif /* DEBUG || _DEBUG */
cursor = rrlist;
/* main loop */ /* main loop */
main_loop();
finish();
return 0;
} /* main() */
while( num_jobs ) void main_loop()
{ {
/* send any pings that need sending */ u_int lt;
if(sq_first) { long wait_time;
#if defined( DEBUG ) || defined( _DEBUG ) HOST_ENTRY *h;
if( trace_flag ) {
long st = timeval_diff(&sq_first->sq_send_time, &current_time);
fprintf(stderr, "next ping in %d ms (%s)\n", st / 100, sq_first->host);
}
#endif
if(sq_first->sq_send_time.tv_sec < current_time.tv_sec || while(ev_first) {
(sq_first->sq_send_time.tv_sec == current_time.tv_sec && /* Any event that can be processed now ? */
sq_first->sq_send_time.tv_usec < current_time.tv_usec)) if(ev_first->ev_time.tv_sec < current_time.tv_sec ||
(ev_first->ev_time.tv_sec == current_time.tv_sec &&
ev_first->ev_time.tv_usec < current_time.tv_usec))
{ {
/* Event type: ping */
if(ev_first->ev_type == EV_TYPE_PING) {
/* Make sure that we don't ping more than once every "interval" */
lt = timeval_diff( &current_time, &last_send_time ); lt = timeval_diff( &current_time, &last_send_time );
if(lt > interval) { if(lt < interval) goto wait_for_reply;
HOST_ENTRY *h;
h = sq_dequeue(); /* Dequeue the event */
h = ev_dequeue();
/* Send the ping */
if(!send_ping(s, h)) goto wait_for_reply;
if(send_ping(s, h)) { /* Check what needs to be done next */
/* schedule retry */ if(!loop_flag && !count_flag) {
if(!loop_flag && !count_flag && h->waiting < retry + 1) { /* Normal mode: schedule retry */
h->sq_send_time.tv_sec = current_time.tv_sec; if(h->waiting < retry + 1) {
h->sq_send_time.tv_usec = current_time.tv_usec; h->ev_type = EV_TYPE_PING;
timeval_add(&h->sq_send_time, h->timeout); h->ev_time.tv_sec = current_time.tv_sec;
sq_enqueue(h); h->ev_time.tv_usec = current_time.tv_usec;
timeval_add(&h->ev_time, h->timeout);
ev_enqueue(h);
if(backoff_flag) { if(backoff_flag) {
h->timeout *= backoff; h->timeout *= backoff;
} }
} }
/* schedule next ping */ /* Normal mode: schedule timeout for last retry */
else if(loop_flag || else {
(count_flag && h->num_sent < count)) h->ev_type = EV_TYPE_TIMEOUT;
{ h->ev_time.tv_sec = current_time.tv_sec;
h->sq_send_time.tv_sec = current_time.tv_sec; h->ev_time.tv_usec = current_time.tv_usec;
h->sq_send_time.tv_usec = current_time.tv_usec; timeval_add(&h->ev_time, h->timeout);
timeval_add(&h->sq_send_time, perhost_interval); ev_enqueue(h);
sq_enqueue(h);
} }
} }
/* Loop and count mode: schedule next ping */
else if(loop_flag || (count_flag && h->num_sent < count))
{
h->ev_type = EV_TYPE_PING;
h->ev_time.tv_sec = current_time.tv_sec;
h->ev_time.tv_usec = current_time.tv_usec;
timeval_add(&h->ev_time, perhost_interval);
ev_enqueue(h);
} }
/* Count mode: schedule timeout after last ping */
else if(count_flag && count_flag && h->num_sent >= count) {
h->ev_type = EV_TYPE_TIMEOUT;
h->ev_time.tv_sec = current_time.tv_sec;
h->ev_time.tv_usec = current_time.tv_usec;
timeval_add(&h->ev_time, h->timeout);
ev_enqueue(h);
} }
} }
else if(ev_first->ev_type == EV_TYPE_TIMEOUT) {
/* receive replies */ num_timeout++;
if( num_pingsent ) { remove_job(ev_first);
if(wait_for_reply(interval)) {
while(wait_for_reply(0)); /* process other replies in the queue */
} }
} }
gettimeofday( &current_time, &tz ); wait_for_reply:
ht = timeval_diff( &current_time, &cursor->last_send_time );
/* print report */
if( report_interval && ( loop_flag || count_flag ) &&
( timeval_diff ( &current_time, &last_report_time ) > report_interval ) )
{
print_per_system_splits();
last_report_time = current_time;
/* When can we expect the next event? */
if(ev_first) {
if(ev_first->ev_time.tv_sec == 0) {
wait_time = interval;
}
else {
wait_time = timeval_diff(&ev_first->ev_time, &current_time);
if(wait_time < (long) interval) {
wait_time = interval;
}
} }
#if defined( DEBUG ) || defined( _DEBUG ) #if defined( DEBUG ) || defined( _DEBUG )
if( trace_flag ) if( trace_flag ) {
{ fprintf(stderr, "next event in %d ms (%s)\n", wait_time / 100, ev_first->host);
printf(
"main loop:\n [%s, wait/run/sent/recv/timeout = %u/%u/%u/%u/%u], jobs/ht = %u/%u\n",
cursor->host, cursor->waiting, cursor->running, cursor->num_sent,
cursor->num_recv, cursor->timeout, num_jobs, ht );
} }
#endif #endif
/* check if timeout reached */
if(count_flag)
{
while(cursor &&
ht > cursor->timeout &&
cursor->num_sent >= count)
{
num_timeout++;
remove_job(cursor);
if(cursor) {
ht = timeval_diff( &current_time, &cursor->last_send_time );
} }
else {
wait_time = interval;
} }
/* Receive replies */
/* (this is what sleeps during each loop iteration) */
if(wait_for_reply(wait_time)) {
while(wait_for_reply(0)); /* process other replies in the queue */
} }
else if(!loop_flag)
{ gettimeofday( &current_time, &tz );
while(cursor &&
ht > cursor->timeout && /* Print report */
cursor->waiting >= retry + 1) if( report_interval && ( loop_flag || count_flag ) &&
( timeval_diff ( &current_time, &last_report_time ) > report_interval ) )
{ {
num_timeout++; print_per_system_splits();
remove_job( cursor ); last_report_time = current_time;
if(cursor) {
ht = timeval_diff( &current_time, &cursor->last_send_time );
}
} }
} }
/* we use the cursor to go through all running jobs and check for timeouts */ fprintf(stderr, "No further events\n");
if(cursor) {
cursor = cursor->next;
} }
}
finish();
return 0;
} /* main() */
/************************************************************ /************************************************************
@ -2405,26 +2415,11 @@ void add_addr( char *name, char *host, FPING_SOCKADDR *ipaddr )
}/* IF */ }/* IF */
#endif /* DEBUG || _DEBUG */ #endif /* DEBUG || _DEBUG */
if( !rrlist ) /* schedule first ping */
{ p->ev_type = EV_TYPE_PING;
rrlist = p; p->ev_time.tv_sec = 0;
p->next = p; p->ev_time.tv_usec = 0;
p->prev = p; ev_enqueue(p);
}/* IF */
else
{
p->next = rrlist;
p->prev = rrlist->prev;
p->prev->next = p;
p->next->prev = p;
}/* ELSE */
/* send queue */
p->sq_send_time.tv_sec = 0;
p->sq_send_time.tv_usec = 0;
sq_enqueue(p);
num_hosts++; num_hosts++;
@ -2459,23 +2454,7 @@ void remove_job( HOST_ENTRY *h )
h->waiting = 0; h->waiting = 0;
--num_jobs; --num_jobs;
if( num_jobs ) ev_remove(h);
{
/* remove us from list of active jobs */
h->prev->next = h->next;
h->next->prev = h->prev;
if( h==cursor )
cursor = h->next;
}
else
{
cursor = NULL;
rrlist = NULL;
}
sq_remove(h);
} /* remove_job() */ } /* remove_job() */
@ -2633,13 +2612,14 @@ struct timeval *a, *b;
long timeval_diff( struct timeval *a, struct timeval *b ) long timeval_diff( struct timeval *a, struct timeval *b )
#endif /* _NO_PROTO */ #endif /* _NO_PROTO */
{ {
double temp; long sec_diff = a->tv_sec - b->tv_sec;
if(sec_diff > 100) {
temp = ( ( ( a->tv_sec * 1000000 ) + a->tv_usec ) - /* For such large differences, we don't really care about the microseconds... */
( ( b->tv_sec * 1000000 ) + b->tv_usec ) ) / 10; return sec_diff * 100000;
}
return ( long )temp; else {
return (sec_diff * 1000000 + a->tv_usec - b->tv_usec) / 10;
}
} /* timeval_diff() */ } /* timeval_diff() */
/************************************************************ /************************************************************
@ -2805,102 +2785,112 @@ int recvfrom_wto( int s, char *buf, int len, FPING_SOCKADDR *saddr, int timo )
/************************************************************ /************************************************************
Function: sq_enqueue Function: ev_enqueue
Enqueue a host that needs to be pinged, but not before the time Enqueue a host that needs to be pinged, but not before the time
written in h->sq_send_time. written in h->ev_time.
The queue is sorted, so that sq_first always points to the host The queue is sorted, so that ev_first always points to the host
that should be pinged first. that should be pinged first.
We start scanning the queue from the tail, because we assume
that new events mostly get inserted with a event time higher
than the others.
*************************************************************/ *************************************************************/
void sq_enqueue(HOST_ENTRY *h) void ev_enqueue(HOST_ENTRY *h)
{ {
HOST_ENTRY *i; HOST_ENTRY *i;
HOST_ENTRY *i_next; HOST_ENTRY *i_prev;
h->sq_next = NULL; #if defined( DEBUG ) || defined( _DEBUG )
if( trace_flag ) {
long st = timeval_diff(&h->ev_time, &current_time);
fprintf(stderr, "Enqueue: host=%s, when=%d ms (%d, %d)\n", h->host, st/100, h->ev_time.tv_sec, h->ev_time.tv_usec);
}
#endif
/* Empty list */ /* Empty list */
if(sq_first == NULL) { if(ev_last == NULL) {
h->sq_prev = NULL; h->ev_next = NULL;
sq_first = h; h->ev_prev = NULL;
sq_last = h; ev_first = h;
ev_last = h;
return; return;
} }
/* Insert on tail? */
/* Insert on head? */ if(h->ev_time.tv_sec > ev_last->ev_time.tv_sec ||
if(h->sq_send_time.tv_sec < sq_first->sq_send_time.tv_sec || (h->ev_time.tv_sec == ev_last->ev_time.tv_sec &&
(h->sq_send_time.tv_sec == sq_first->sq_send_time.tv_sec && h->ev_time.tv_usec >= ev_last->ev_time.tv_usec))
h->sq_send_time.tv_usec <= sq_first->sq_send_time.tv_usec))
{ {
h->sq_prev = NULL; h->ev_next = NULL;
h->sq_next = sq_first; h->ev_prev = ev_last;
sq_first = h; ev_last->ev_next = h;
ev_last = h;
return; return;
} }
/* Find insertion point */ /* Find insertion point */
i = sq_first; i = ev_last;
while(1) { while(1) {
i_next = i->sq_next; i_prev = i->ev_prev;
if(i_next == NULL || if(i_prev == NULL ||
h->sq_send_time.tv_sec < i_next->sq_send_time.tv_sec || h->ev_time.tv_sec > i_prev->ev_time.tv_sec ||
(h->sq_send_time.tv_sec == i_next->sq_send_time.tv_sec && (h->ev_time.tv_sec == i_prev->ev_time.tv_sec &&
h->sq_send_time.tv_usec < i_next->sq_send_time.tv_usec)) h->ev_time.tv_usec >= i_prev->ev_time.tv_usec))
{ {
h->sq_prev = i; h->ev_prev = i_prev;
h->sq_next = i_next; h->ev_next = i;
i->sq_next = h; i->ev_prev = h;
if(i_next != NULL) { if(i_prev != NULL) {
i_next->sq_prev = h; i_prev->ev_next = h;
} }
else { else {
sq_last = h; ev_first = h;
} }
return; return;
} }
i = i_next; i = i_prev;
} }
} }
/************************************************************ /************************************************************
Function: sq_dequeue Function: ev_dequeue
*************************************************************/ *************************************************************/
HOST_ENTRY *sq_dequeue() HOST_ENTRY *ev_dequeue()
{ {
HOST_ENTRY *dequeued; HOST_ENTRY *dequeued;
if(sq_first == NULL) { if(ev_first == NULL) {
return NULL; return NULL;
} }
dequeued = sq_first; dequeued = ev_first;
sq_remove(dequeued); ev_remove(dequeued);
return dequeued; return dequeued;
} }
/************************************************************ /************************************************************
Function: sq_remove Function: ev_remove
*************************************************************/ *************************************************************/
void sq_remove(HOST_ENTRY *h) void ev_remove(HOST_ENTRY *h)
{ {
if(sq_first == h) { if(ev_first == h) {
sq_first = h->sq_next; ev_first = h->ev_next;
} }
if(sq_last == h) { if(ev_last == h) {
sq_last = h->sq_prev; ev_last = h->ev_prev;
} }
if(h->sq_prev) { if(h->ev_prev) {
h->sq_prev->sq_next = h->sq_next; h->ev_prev->ev_next = h->ev_next;
} }
if(h->sq_next) { if(h->ev_next) {
h->sq_next->sq_prev = h->sq_prev; h->ev_next->ev_prev = h->ev_prev;
} }
} }

Loading…
Cancel
Save