[MAJOR] replaced all timeouts with struct timeval

The timeout functions were difficult to manipulate because they were
rounding results to the millisecond. Thus, it was difficult to compare
and to check what expired and what did not. Also, the comparison
functions were heavy with multiplies and divides by 1000. Now, all
timeouts are stored in timevals, reducing the number of operations
for updates and leading to cleaner and more efficient code.
This commit is contained in:
Willy Tarreau 2007-05-12 22:35:00 +02:00
parent 49fa3a1453
commit d825eef9c5
26 changed files with 225 additions and 190 deletions

View File

@ -44,7 +44,7 @@ void destroy(void *data);
static void print_table(const CHTbl *htbl);
#endif
int appsession_refresh(struct task *t);
void appsession_refresh(struct task *t, struct timeval *next);
int appsession_task_init(void);
int appsession_init(void);
void appsession_cleanup(void);

View File

@ -2,7 +2,7 @@
include/proto/checks.h
Functions prototypes for the checks.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -25,7 +25,7 @@
#include <types/task.h>
#include <common/config.h>
int process_chk(struct task *t);
void process_chk(struct task *t, struct timeval *next);
#endif /* _PROTO_CHECKS_H */

View File

@ -58,7 +58,7 @@ extern const char http_is_ver_token[256];
#define HTTP_IS_VER_TOKEN(x) (http_is_ver_token[(unsigned char)(x)])
int event_accept(int fd);
int process_session(struct task *t);
void process_session(struct task *t, struct timeval *next);
int process_cli(struct session *t);
int process_srv(struct session *t);

View File

@ -2,7 +2,7 @@
include/proto/proxy.h
This file defines function prototypes for proxy management.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -23,10 +23,11 @@
#define _PROTO_PROXY_H
#include <common/config.h>
#include <common/time.h>
#include <types/proxy.h>
int start_proxies(int verbose);
int maintain_proxies(void);
void maintain_proxies(struct timeval *next);
void soft_stop(void);
void pause_proxy(struct proxy *p);
void pause_proxies(void);

View File

@ -2,7 +2,7 @@
include/proto/queue.h
This file defines everything related to queues.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -35,7 +35,7 @@
struct session *pendconn_get_next_sess(struct server *srv, struct proxy *px);
struct pendconn *pendconn_add(struct session *sess);
void pendconn_free(struct pendconn *p);
int process_srv_queue(struct task *t);
void process_srv_queue(struct task *t, struct timeval *next);
unsigned int srv_dynamic_maxconn(const struct server *s);

View File

@ -111,12 +111,10 @@ struct task *task_queue(struct task *task);
* - wake up all expired tasks
* - call all runnable tasks
* - call maintain_proxies() to enable/disable the listeners
* - return the delay till next event in ms, -1 = wait indefinitely
* Note: this part should be rewritten with the O(ln(n)) scheduler.
*
* - return the date of next event in <next> or eternity.
*/
int process_runnable_tasks();
void process_runnable_tasks(struct timeval *next);
#endif /* _PROTO_TASK_H */

View File

@ -63,9 +63,9 @@ struct buffer {
struct timeval rex; /* expiration date for a read */
struct timeval wex; /* expiration date for a write */
struct timeval cex; /* expiration date for a connect */
int rto; /* read timeout */
int wto; /* write timeout */
int cto; /* connect timeout */
struct timeval rto; /* read timeout */
struct timeval wto; /* write timeout */
struct timeval cto; /* connect timeout */
unsigned int l; /* data length */
char *r, *w, *lr; /* read ptr, write ptr, last read */
char *rlim; /* read limit, used for header rewriting */

View File

@ -81,7 +81,7 @@ struct fdtab {
* it returns 0. It may be the same as clr().
* - clo() should be used to do indicate the poller that fd will be closed. It
* may be the same as rem() on some pollers.
* - poll() calls the poller, waiting at most wait_time ms.
* - poll() calls the poller, expiring at <exp>
*/
struct poller {
void *private; /* any private data for the poller */
@ -92,7 +92,7 @@ struct poller {
int REGPRM2 (*cond_c)(const int fd, int dir); /* clear polling on <fd> for <dir> if set */
void REGPRM1 (*rem)(const int fd); /* remove any polling on <fd> */
void REGPRM1 (*clo)(const int fd); /* mark <fd> as closed */
void REGPRM2 (*poll)(struct poller *p, int wait_time); /* the poller itself */
void REGPRM2 (*poll)(struct poller *p, struct timeval *exp); /* the poller itself */
int REGPRM1 (*init)(struct poller *p); /* poller initialization */
void REGPRM1 (*term)(struct poller *p); /* termination of this poller */
int REGPRM1 (*test)(struct poller *p); /* pre-init check of the poller */

View File

@ -96,7 +96,6 @@ struct proxy {
char *appsession_name; /* name of the cookie to look for */
int appsession_name_len; /* strlen(appsession_name), computed only once */
int appsession_len; /* length of the appsession cookie value to be used */
int appsession_timeout;
CHTbl htbl_proxy; /* Per Proxy hashtable */
char *capture_name; /* beginning of the name of the cookie to capture */
int capture_namelen; /* length of the cookie name to match */
@ -104,9 +103,10 @@ struct proxy {
struct uri_auth *uri_auth; /* if non-NULL, the (list of) per-URI authentications */
char *monitor_uri; /* a special URI to which we respond with HTTP/200 OK */
int monitor_uri_len; /* length of the string above. 0 if unused */
int clitimeout; /* client I/O timeout (in milliseconds) */
int srvtimeout; /* server I/O timeout (in milliseconds) */
int contimeout; /* connect timeout (in milliseconds) */
struct timeval clitimeout; /* client I/O timeout (in milliseconds) */
struct timeval srvtimeout; /* server I/O timeout (in milliseconds) */
struct timeval contimeout; /* connect timeout (in milliseconds) */
struct timeval appsession_timeout;
char *id; /* proxy id */
struct list pendconns; /* pending connections with no server assigned yet */
int nbpend, nbpend_max; /* number of pending connections with no server assigned yet */

View File

@ -2,7 +2,7 @@
include/types/task.h
Macros, variables and structures for task management.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -38,7 +38,7 @@ struct task {
struct ultree *wq; /* NULL if unqueued, or back ref to the carrier node in the WQ */
int state; /* task state : IDLE or RUNNING */
struct timeval expire; /* next expiration time for this task, use only for fast sorting */
int (*process)(struct task *t); /* the function which processes the task */
void (*process)(struct task *t, struct timeval *next); /* the function which processes the task */
void *context; /* the task's context */
};

View File

@ -125,7 +125,7 @@ int appsession_task_init(void)
return 0;
}
int appsession_refresh(struct task *t)
void appsession_refresh(struct task *t, struct timeval *next)
{
struct proxy *p = proxy;
CHTbl *htbl;
@ -143,7 +143,7 @@ int appsession_refresh(struct task *t)
for (element = list_head(&htbl->table[i]);
element != NULL; element = list_next(element)) {
asession = (appsess *)list_data(element);
if (tv_ms_le2(&asession->expire, &now)) {
if (__tv_isle(&asession->expire, &now)) {
if ((global.mode & MODE_DEBUG) &&
(!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len;
@ -165,7 +165,7 @@ int appsession_refresh(struct task *t)
}
else
element = last;
}/* end if (tv_ms_le2(&asession->expire, &now)) */
}/* end if (__tv_isle(&asession->expire, &now)) */
else
last = element;
}/* end for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) */
@ -174,7 +174,7 @@ int appsession_refresh(struct task *t)
p = p->next;
}
tv_ms_add(&t->expire, &now, TBLCHKINT); /* check expiration every 5 seconds */
return TBLCHKINT;
*next = t->expire;
} /* end appsession_refresh */
int match_str(const void *key1, const void *key2)

View File

@ -1,7 +1,7 @@
/*
* Backend variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -550,8 +550,8 @@ int connect_server(struct session *s)
s->srv->cur_sess_max = s->srv->cur_sess;
}
if (s->be->contimeout)
tv_ms_add(&s->req->cex, &now, s->be->contimeout);
if (tv_isset(&s->be->contimeout))
tv_add(&s->req->cex, &now, &s->be->contimeout);
else
tv_eternity(&s->req->cex);
return SN_ERR_NONE; /* connection is OK */
@ -680,8 +680,8 @@ int srv_redispatch_connect(struct session *t)
case SRV_STATUS_QUEUED:
/* FIXME-20060503 : we should use the queue timeout instead */
if (t->be->contimeout)
tv_ms_add(&t->req->cex, &now, t->be->contimeout);
if (tv_isset(&t->be->contimeout))
tv_add(&t->req->cex, &now, &t->be->contimeout);
else
tv_eternity(&t->req->cex);
t->srv_state = SV_STIDLE;

View File

@ -766,7 +766,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
curproxy->appsession_name = strdup(args[1]);
curproxy->appsession_name_len = strlen(curproxy->appsession_name);
curproxy->appsession_len = atoi(args[3]);
curproxy->appsession_timeout = atoi(args[5]);
__tv_from_ms(&curproxy->appsession_timeout, atoi(args[5]));
rc = chtbl_init(&(curproxy->htbl_proxy), TBLSIZ, hashpjw, match_str, destroy);
if (rc) {
Alert("Error Init Appsession Hashtable.\n");
@ -857,7 +857,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
}
}
else if (!strcmp(args[0], "contimeout")) { /* connect timeout */
if (curproxy->contimeout != defproxy.contimeout) {
if (!__tv_iseq(&curproxy->contimeout, &defproxy.contimeout)) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
return 0;
}
@ -869,10 +869,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
file, linenum, args[0]);
return -1;
}
curproxy->contimeout = atol(args[1]);
__tv_from_ms(&curproxy->contimeout, atol(args[1]));
}
else if (!strcmp(args[0], "clitimeout")) { /* client timeout */
if (curproxy->clitimeout != defproxy.clitimeout) {
if (!__tv_iseq(&curproxy->clitimeout, &defproxy.clitimeout)) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
file, linenum, args[0]);
return 0;
@ -885,10 +885,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
file, linenum, args[0]);
return -1;
}
curproxy->clitimeout = atol(args[1]);
__tv_from_ms(&curproxy->clitimeout, atol(args[1]));
}
else if (!strcmp(args[0], "srvtimeout")) { /* server timeout */
if (curproxy->srvtimeout != defproxy.srvtimeout) {
if (!__tv_iseq(&curproxy->srvtimeout, &defproxy.srvtimeout)) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
return 0;
}
@ -900,7 +900,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
file, linenum, args[0]);
return -1;
}
curproxy->srvtimeout = atol(args[1]);
__tv_from_ms(&curproxy->srvtimeout, atol(args[1]));
}
else if (!strcmp(args[0], "retries")) { /* connection retries */
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
@ -2385,8 +2385,9 @@ int readcfgfile(const char *file)
}
}
if ((curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) &&
(((curproxy->cap & PR_CAP_FE) && !curproxy->clitimeout) ||
((curproxy->cap & PR_CAP_BE) && (curproxy->srv) && (!curproxy->contimeout || !curproxy->srvtimeout)))) {
(((curproxy->cap & PR_CAP_FE) && !tv_isset(&curproxy->clitimeout)) ||
((curproxy->cap & PR_CAP_BE) && (curproxy->srv) &&
(!tv_isset(&curproxy->contimeout) || !tv_isset(&curproxy->srvtimeout))))) {
Warning("parsing %s : missing timeouts for %s '%s'.\n"
" | While not properly invalid, you will certainly encounter various problems\n"
" | with such a configuration. To fix this, please ensure that all following\n"

View File

@ -1,7 +1,7 @@
/*
* Health-checks functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -275,13 +275,12 @@ static int event_srv_chk_r(int fd)
* manages a server health-check. Returns
* the time the task accepts to wait, or TIME_ETERNITY for infinity.
*/
int process_chk(struct task *t)
void process_chk(struct task *t, struct timeval *next)
{
__label__ new_chk, out;
struct server *s = t->context;
struct sockaddr_in sa;
int fd;
int next_time;
//fprintf(stderr, "process_chk: task=%p\n", t);
@ -289,9 +288,9 @@ int process_chk(struct task *t)
fd = s->curfd;
if (fd < 0) { /* no check currently running */
//fprintf(stderr, "process_chk: 2\n");
if (!tv_ms_le2(&t->expire, &now)) { /* not good time yet */
if (!__tv_isle(&t->expire, &now)) { /* not good time yet */
task_queue(t); /* restore t to its place in the task list */
next_time = tv_ms_remain2(&now, &t->expire);
*next = t->expire;
goto out;
}
@ -299,10 +298,10 @@ int process_chk(struct task *t)
* the server should not be checked.
*/
if (!(s->state & SRV_CHECKED) || s->proxy->state == PR_STSTOPPED) {
while (tv_ms_le2(&t->expire, &now))
while (__tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter);
task_queue(t); /* restore t to its place in the task list */
next_time = tv_ms_remain2(&now, &t->expire);
*next = t->expire;
goto out;
}
@ -410,7 +409,7 @@ int process_chk(struct task *t)
/* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
tv_ms_add(&t->expire, &now, s->inter);
task_queue(t); /* restore t to its place in the task list */
return tv_ms_remain(&now, &t->expire);
*next = t->expire;
}
else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
s->result = -1; /* a real error */
@ -422,7 +421,7 @@ int process_chk(struct task *t)
if (!s->result) { /* nothing done */
//fprintf(stderr, "process_chk: 6\n");
while (tv_ms_le2(&t->expire, &now))
while (__tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter);
goto new_chk; /* may be we should initialize a new check */
}
@ -437,7 +436,7 @@ int process_chk(struct task *t)
//fprintf(stderr, "process_chk: 7\n");
/* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
while (tv_ms_le2(&t->expire, &now))
while (__tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter);
goto new_chk;
}
@ -488,11 +487,11 @@ int process_chk(struct task *t)
}
s->curfd = -1; /* no check running anymore */
fd_delete(fd);
while (tv_ms_le2(&t->expire, &now))
while (__tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter);
goto new_chk;
}
else if (s->result < 0 || tv_ms_le2(&t->expire, &now)) {
else if (s->result < 0 || __tv_isle(&t->expire, &now)) {
//fprintf(stderr, "process_chk: 10\n");
/* failure or timeout detected */
if (s->health > s->rise) {
@ -503,7 +502,7 @@ int process_chk(struct task *t)
set_server_down(s);
s->curfd = -1;
fd_delete(fd);
while (tv_ms_le2(&t->expire, &now))
while (__tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter);
goto new_chk;
}
@ -512,12 +511,9 @@ int process_chk(struct task *t)
//fprintf(stderr, "process_chk: 11\n");
s->result = 0;
task_queue(t); /* restore t to its place in the task list */
next_time = tv_ms_remain2(&now, &t->expire);
*next = t->expire;
out:
/* Ensure that we don't report sub-millisecond timeouts */
if (next_time != TIME_ETERNITY)
next_time++;
return next_time;
return;
}

View File

@ -1,7 +1,7 @@
/*
* Client-side variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -386,7 +386,7 @@ int event_accept(int fd) {
s->rep->rto = s->be->srvtimeout;
s->rep->wto = s->fe->clitimeout;
s->rep->cto = 0;
tv_zero(&s->rep->cto);
fd_insert(cfd);
fdtab[cfd].owner = t;
@ -421,13 +421,13 @@ int event_accept(int fd) {
tv_eternity(&s->rep->wex);
tv_eternity(&t->expire);
if (s->fe->clitimeout) {
if (tv_isset(&s->fe->clitimeout)) {
if (EV_FD_ISSET(cfd, DIR_RD)) {
tv_ms_add(&s->req->rex, &now, s->fe->clitimeout);
tv_add(&s->req->rex, &now, &s->fe->clitimeout);
t->expire = s->req->rex;
}
if (EV_FD_ISSET(cfd, DIR_WR)) {
tv_ms_add(&s->rep->wex, &now, s->fe->clitimeout);
tv_add(&s->rep->wex, &now, &s->fe->clitimeout);
t->expire = s->req->rex;
}
}

View File

@ -220,16 +220,22 @@ REGPRM1 static void __fd_clo(int fd)
/*
* epoll() poller
*/
REGPRM2 static void _do_poll(struct poller *p, int wait_time)
REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
{
int status;
int fd;
int count;
int wait_time;
if (likely(nbchanges))
fd_flush_changes();
/* now let's wait for events */
if (tv_isset(exp))
wait_time = tv_ms_remain(&now, exp);
else
wait_time = -1;
status = epoll_wait(epoll_fd, epoll_events, maxfd, wait_time);
tv_now(&now);

View File

@ -81,10 +81,11 @@ REGPRM1 static void __fd_rem(const int fd)
/*
* Poll() poller
*/
REGPRM2 static void _do_poll(struct poller *p, int wait_time)
REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
{
int status;
int fd, nbfd;
int wait_time;
int fds, count;
int sr, sw;
@ -123,6 +124,11 @@ REGPRM2 static void _do_poll(struct poller *p, int wait_time)
}
/* now let's wait for events */
if (tv_isset(exp))
wait_time = tv_ms_remain(&now, exp);
else
wait_time = -1;
status = poll(poll_events, nbfd, wait_time);
tv_now(&now);

View File

@ -78,7 +78,7 @@ REGPRM1 static void __fd_rem(int fd)
/*
* Select() poller
*/
REGPRM2 static void _do_poll(struct poller *p, int wait_time)
REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
{
int status;
int fd, i;
@ -89,12 +89,14 @@ REGPRM2 static void _do_poll(struct poller *p, int wait_time)
/* allow select to return immediately when needed */
delta.tv_sec = delta.tv_usec = 0;
if (wait_time > 0) { /* FIXME */
/* Convert to timeval */
/* to avoid eventual select loops due to timer precision */
wait_time += SCHEDULER_RESOLUTION;
delta.tv_sec = wait_time / 1000;
delta.tv_usec = (wait_time % 1000) * 1000;
if (tv_isset(exp)) {
tv_remain(&now, exp, &delta);
/* To avoid eventual select loops due to timer precision */
delta.tv_usec += SCHEDULER_RESOLUTION * 1000;
if (delta.tv_usec >= 1000000) {
delta.tv_usec -= 1000000;
delta.tv_sec ++;
}
}
/* let's restore fdset state */
@ -118,7 +120,7 @@ REGPRM2 static void _do_poll(struct poller *p, int wait_time)
readnotnull ? tmp_evts[DIR_RD] : NULL,
writenotnull ? tmp_evts[DIR_WR] : NULL,
NULL,
(wait_time >= 0) ? &delta : NULL);
tv_isset(exp) ? &delta : NULL);
tv_now(&now);

View File

@ -259,13 +259,14 @@ static struct ev_to_epoll {
/*
* speculative epoll() poller
*/
REGPRM2 static void _do_poll(struct poller *p, int wait_time)
REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
{
static unsigned int last_skipped;
int status;
int fd, opcode;
int count;
int spec_idx;
int wait_time;
/* Here we have two options :
@ -370,6 +371,12 @@ REGPRM2 static void _do_poll(struct poller *p, int wait_time)
}
wait_time = 0;
}
else {
if (tv_isset(exp))
wait_time = tv_ms_remain(&now, exp);
else
wait_time = -1;
}
last_skipped = 0;
/* now let's wait for events */

View File

@ -684,17 +684,17 @@ static void tell_old_pids(int sig)
*/
void run_poll_loop()
{
int next_time;
tv_now(&now);
struct timeval next;
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
process_runnable_tasks(&next);
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
cur_poller.poll(&cur_poller, next_time);
cur_poller.poll(&cur_poller, &next);
}
}

View File

@ -442,8 +442,8 @@ void client_retnclose(struct session *s, const struct chunk *msg)
EV_FD_CLR(s->cli_fd, DIR_RD);
EV_FD_SET(s->cli_fd, DIR_WR);
tv_eternity(&s->req->rex);
if (s->fe->clitimeout)
tv_ms_add(&s->rep->wex, &now, s->fe->clitimeout);
if (tv_isset(&s->fe->clitimeout))
tv_add(&s->rep->wex, &now, &s->fe->clitimeout);
else
tv_eternity(&s->rep->wex);
s->cli_state = CL_STSHUTR;
@ -532,7 +532,7 @@ static http_meth_t find_http_meth(const char *str, const int len)
* the time the task accepts to wait, or TIME_ETERNITY for
* infinity.
*/
int process_session(struct task *t)
void process_session(struct task *t, struct timeval *next)
{
struct session *s = t->context;
int fsm_resync = 0;
@ -563,11 +563,11 @@ int process_session(struct task *t)
/* DEBUG code : this should never ever happen, otherwise it indicates
* that a task still has something to do and will provoke a quick loop.
*/
if (tv_ms_remain2(&now, &t->expire) <= 0)
if (tv_remain2(&now, &t->expire) <= 0)
exit(100);
#endif
return tv_ms_remain2(&now, &t->expire); /* nothing more to do */
*next = t->expire;
return; /* nothing more to do */
}
s->fe->feconn--;
@ -615,7 +615,7 @@ int process_session(struct task *t)
task_delete(t);
session_free(s);
task_free(t);
return TIME_ETERNITY; /* rest in peace for eternity */
tv_eternity(next);
}
@ -1529,7 +1529,7 @@ int process_cli(struct session *t)
}
/* 3: has the read timeout expired ? */
else if (unlikely(tv_ms_le2(&req->rex, &now))) {
else if (unlikely(__tv_isle(&req->rex, &now))) {
/* read timeout : give up with an error message. */
txn->status = 408;
client_retnclose(t, error_message(t, HTTP_ERR_408));
@ -1547,8 +1547,8 @@ int process_cli(struct session *t)
* full. We cannot loop here since stream_sock_read will disable it only if
* req->l == rlim-data
*/
if (t->fe->clitimeout)
tv_ms_add(&req->rex, &now, t->fe->clitimeout);
if (tv_isset(&t->fe->clitimeout))
tv_add(&req->rex, &now, &t->fe->clitimeout);
else
tv_eternity(&req->rex);
}
@ -1912,8 +1912,8 @@ int process_cli(struct session *t)
t->logs.t_request = tv_ms_elapsed(&t->logs.tv_accept, &now);
if (!t->fe->clitimeout ||
(t->srv_state < SV_STDATA && t->be->srvtimeout)) {
if (!tv_isset(&t->fe->clitimeout) ||
(t->srv_state < SV_STDATA && tv_isset(&t->be->srvtimeout))) {
/* If the client has no timeout, or if the server is not ready yet,
* and we know for sure that it can expire, then it's cleaner to
* disable the timeout on the client side so that too low values
@ -1936,8 +1936,10 @@ int process_cli(struct session *t)
/* flush the request so that we can drop the connection early
* if the client closes first.
*/
tv_ms_add(&req->cex, &now,
t->be->contimeout ? t->be->contimeout : 0);
if (tv_isset(&t->be->contimeout))
tv_add(&req->cex, &now, &t->be->contimeout);
else
req->cex = now;
}
/* OK let's go on with the BODY now */
@ -1996,14 +1998,14 @@ int process_cli(struct session *t)
/* We must ensure that the read part is still alive when switching
* to shutw */
EV_FD_SET(t->cli_fd, DIR_RD);
if (t->fe->clitimeout)
tv_ms_add(&req->rex, &now, t->fe->clitimeout);
if (tv_isset(&t->fe->clitimeout))
tv_add(&req->rex, &now, &t->fe->clitimeout);
t->cli_state = CL_STSHUTW;
//fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
return 1;
}
/* read timeout */
else if (tv_ms_le2(&req->rex, &now)) {
else if (__tv_isle(&req->rex, &now)) {
EV_FD_CLR(t->cli_fd, DIR_RD);
tv_eternity(&req->rex);
t->cli_state = CL_STSHUTR;
@ -2020,15 +2022,15 @@ int process_cli(struct session *t)
return 1;
}
/* write timeout */
else if (tv_ms_le2(&rep->wex, &now)) {
else if (__tv_isle(&rep->wex, &now)) {
EV_FD_CLR(t->cli_fd, DIR_WR);
tv_eternity(&rep->wex);
shutdown(t->cli_fd, SHUT_WR);
/* We must ensure that the read part is still alive when switching
* to shutw */
EV_FD_SET(t->cli_fd, DIR_RD);
if (t->fe->clitimeout)
tv_ms_add(&req->rex, &now, t->fe->clitimeout);
if (tv_isset(&t->fe->clitimeout))
tv_add(&req->rex, &now, &t->fe->clitimeout);
t->cli_state = CL_STSHUTW;
if (!(t->flags & SN_ERR_MASK))
@ -2053,8 +2055,8 @@ int process_cli(struct session *t)
} else {
/* there's still some space in the buffer */
if (EV_FD_COND_S(t->cli_fd, DIR_RD)) {
if (!t->fe->clitimeout ||
(t->srv_state < SV_STDATA && t->be->srvtimeout))
if (!tv_isset(&t->fe->clitimeout) ||
(t->srv_state < SV_STDATA && tv_isset(&t->be->srvtimeout)))
/* If the client has no timeout, or if the server not ready yet, and we
* know for sure that it can expire, then it's cleaner to disable the
* timeout on the client side so that too low values cannot make the
@ -2062,7 +2064,7 @@ int process_cli(struct session *t)
*/
tv_eternity(&req->rex);
else
tv_ms_add(&req->rex, &now, t->fe->clitimeout);
tv_add(&req->rex, &now, &t->fe->clitimeout);
}
}
@ -2076,8 +2078,8 @@ int process_cli(struct session *t)
/* buffer not empty */
if (EV_FD_COND_S(t->cli_fd, DIR_WR)) {
/* restart writing */
if (t->fe->clitimeout) {
tv_ms_add(&rep->wex, &now, t->fe->clitimeout);
if (tv_isset(&t->fe->clitimeout)) {
tv_add(&rep->wex, &now, &t->fe->clitimeout);
/* FIXME: to prevent the client from expiring read timeouts during writes,
* we refresh it. */
req->rex = rep->wex;
@ -2112,7 +2114,7 @@ int process_cli(struct session *t)
t->cli_state = CL_STCLOSE;
return 1;
}
else if (tv_ms_le2(&rep->wex, &now)) {
else if (__tv_isle(&rep->wex, &now)) {
tv_eternity(&rep->wex);
fd_delete(t->cli_fd);
t->cli_state = CL_STCLOSE;
@ -2149,8 +2151,8 @@ int process_cli(struct session *t)
/* buffer not empty */
if (EV_FD_COND_S(t->cli_fd, DIR_WR)) {
/* restart writing */
if (t->fe->clitimeout) {
tv_ms_add(&rep->wex, &now, t->fe->clitimeout);
if (tv_isset(&t->fe->clitimeout)) {
tv_add(&rep->wex, &now, &t->fe->clitimeout);
/* FIXME: to prevent the client from expiring read timeouts during writes,
* we refresh it. */
req->rex = rep->wex;
@ -2184,7 +2186,7 @@ int process_cli(struct session *t)
t->cli_state = CL_STCLOSE;
return 1;
}
else if (tv_ms_le2(&req->rex, &now)) {
else if (__tv_isle(&req->rex, &now)) {
tv_eternity(&req->rex);
fd_delete(t->cli_fd);
t->cli_state = CL_STCLOSE;
@ -2215,8 +2217,8 @@ int process_cli(struct session *t)
} else {
/* there's still some space in the buffer */
if (EV_FD_COND_S(t->cli_fd, DIR_RD)) {
if (t->fe->clitimeout)
tv_ms_add(&req->rex, &now, t->fe->clitimeout);
if (tv_isset(&t->fe->clitimeout))
tv_add(&req->rex, &now, &t->fe->clitimeout);
else
tv_eternity(&req->rex);
//fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
@ -2281,7 +2283,7 @@ int process_srv(struct session *t)
* already set the connect expiration date to the right
* timeout. We just have to check that it has not expired.
*/
if (!tv_ms_le2(&req->cex, &now))
if (!__tv_isle(&req->cex, &now))
return 0;
/* We will set the queue timer to the time spent, just for
@ -2303,7 +2305,7 @@ int process_srv(struct session *t)
* to any other session to release it and wake us up again.
*/
if (t->pend_pos) {
if (!tv_ms_le2(&req->cex, &now))
if (!__tv_isle(&req->cex, &now))
return 0;
else {
/* we've been waiting too long here */
@ -2349,7 +2351,7 @@ int process_srv(struct session *t)
srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
return 1;
}
if (!(req->flags & BF_WRITE_STATUS) && !tv_ms_le2(&req->cex, &now)) {
if (!(req->flags & BF_WRITE_STATUS) && !__tv_isle(&req->cex, &now)) {
//fprintf(stderr,"1: c=%d, s=%d, now=%d.%06d, exp=%d.%06d\n", c, s, now.tv_sec, now.tv_usec, req->cex.tv_sec, req->cex.tv_usec);
return 0; /* nothing changed */
}
@ -2416,8 +2418,8 @@ int process_srv(struct session *t)
tv_eternity(&req->wex);
} else /* need the right to write */ {
EV_FD_SET(t->srv_fd, DIR_WR);
if (t->be->srvtimeout) {
tv_ms_add(&req->wex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout)) {
tv_add(&req->wex, &now, &t->be->srvtimeout);
/* FIXME: to prevent the server from expiring read timeouts during writes,
* we refresh it. */
rep->rex = req->wex;
@ -2428,8 +2430,8 @@ int process_srv(struct session *t)
if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
EV_FD_SET(t->srv_fd, DIR_RD);
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
else
tv_eternity(&rep->rex);
@ -2517,8 +2519,8 @@ int process_srv(struct session *t)
* full. We cannot loop here since stream_sock_read will disable it only if
* rep->l == rlim-data
*/
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
else
tv_eternity(&rep->rex);
}
@ -2584,7 +2586,7 @@ int process_srv(struct session *t)
/* read timeout : return a 504 to the client.
*/
else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_RD) &&
tv_ms_le2(&rep->rex, &now))) {
__tv_isle(&rep->rex, &now))) {
tv_eternity(&rep->rex);
tv_eternity(&req->wex);
fd_delete(t->srv_fd);
@ -2624,8 +2626,8 @@ int process_srv(struct session *t)
/* We must ensure that the read part is still
* alive when switching to shutw */
EV_FD_SET(t->srv_fd, DIR_RD);
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
shutdown(t->srv_fd, SHUT_WR);
t->srv_state = SV_STSHUTW;
@ -2638,15 +2640,15 @@ int process_srv(struct session *t)
* some work to do on the headers.
*/
else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_WR) &&
tv_ms_le2(&req->wex, &now))) {
__tv_isle(&req->wex, &now))) {
EV_FD_CLR(t->srv_fd, DIR_WR);
tv_eternity(&req->wex);
shutdown(t->srv_fd, SHUT_WR);
/* We must ensure that the read part is still alive
* when switching to shutw */
EV_FD_SET(t->srv_fd, DIR_RD);
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
t->srv_state = SV_STSHUTW;
if (!(t->flags & SN_ERR_MASK))
@ -2667,8 +2669,8 @@ int process_srv(struct session *t)
else if (likely(req->l)) {
if (EV_FD_COND_S(t->srv_fd, DIR_WR)) {
/* restart writing */
if (t->be->srvtimeout) {
tv_ms_add(&req->wex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout)) {
tv_add(&req->wex, &now, &t->be->srvtimeout);
/* FIXME: to prevent the server from expiring read timeouts during writes,
* we refresh it. */
rep->rex = req->wex;
@ -2964,8 +2966,8 @@ int process_srv(struct session *t)
/* We must ensure that the read part is still alive when switching
* to shutw */
EV_FD_SET(t->srv_fd, DIR_RD);
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
shutdown(t->srv_fd, SHUT_WR);
t->srv_state = SV_STSHUTW;
@ -3034,14 +3036,14 @@ int process_srv(struct session *t)
/* We must ensure that the read part is still alive when switching
* to shutw */
EV_FD_SET(t->srv_fd, DIR_RD);
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
t->srv_state = SV_STSHUTW;
return 1;
}
/* read timeout */
else if (tv_ms_le2(&rep->rex, &now)) {
else if (__tv_isle(&rep->rex, &now)) {
EV_FD_CLR(t->srv_fd, DIR_RD);
tv_eternity(&rep->rex);
t->srv_state = SV_STSHUTR;
@ -3052,15 +3054,15 @@ int process_srv(struct session *t)
return 1;
}
/* write timeout */
else if (tv_ms_le2(&req->wex, &now)) {
else if (__tv_isle(&req->wex, &now)) {
EV_FD_CLR(t->srv_fd, DIR_WR);
tv_eternity(&req->wex);
shutdown(t->srv_fd, SHUT_WR);
/* We must ensure that the read part is still alive when switching
* to shutw */
EV_FD_SET(t->srv_fd, DIR_RD);
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
t->srv_state = SV_STSHUTW;
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_SRVTO;
@ -3079,8 +3081,8 @@ int process_srv(struct session *t)
else { /* buffer not empty, there are still data to be transferred */
if (EV_FD_COND_S(t->srv_fd, DIR_WR)) {
/* restart writing */
if (t->be->srvtimeout) {
tv_ms_add(&req->wex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout)) {
tv_add(&req->wex, &now, &t->be->srvtimeout);
/* FIXME: to prevent the server from expiring read timeouts during writes,
* we refresh it. */
rep->rex = req->wex;
@ -3098,8 +3100,8 @@ int process_srv(struct session *t)
}
else {
if (EV_FD_COND_S(t->srv_fd, DIR_RD)) {
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
else
tv_eternity(&rep->rex);
}
@ -3147,7 +3149,7 @@ int process_srv(struct session *t)
return 1;
}
else if (tv_ms_le2(&req->wex, &now)) {
else if (__tv_isle(&req->wex, &now)) {
//EV_FD_CLR(t->srv_fd, DIR_WR);
tv_eternity(&req->wex);
fd_delete(t->srv_fd);
@ -3176,8 +3178,8 @@ int process_srv(struct session *t)
else { /* buffer not empty */
if (EV_FD_COND_S(t->srv_fd, DIR_WR)) {
/* restart writing */
if (t->be->srvtimeout) {
tv_ms_add(&req->wex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout)) {
tv_add(&req->wex, &now, &t->be->srvtimeout);
/* FIXME: to prevent the server from expiring read timeouts during writes,
* we refresh it. */
rep->rex = req->wex;
@ -3228,7 +3230,7 @@ int process_srv(struct session *t)
return 1;
}
else if (tv_ms_le2(&rep->rex, &now)) {
else if (__tv_isle(&rep->rex, &now)) {
//EV_FD_CLR(t->srv_fd, DIR_RD);
tv_eternity(&rep->rex);
fd_delete(t->srv_fd);
@ -3255,8 +3257,8 @@ int process_srv(struct session *t)
}
else {
if (EV_FD_COND_S(t->srv_fd, DIR_RD)) {
if (t->be->srvtimeout)
tv_ms_add(&rep->rex, &now, t->be->srvtimeout);
if (tv_isset(&t->be->srvtimeout))
tv_add(&rep->rex, &now, &t->be->srvtimeout);
else
tv_eternity(&rep->rex);
}
@ -4375,7 +4377,7 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
}/* end while(srv) */
}/* end else if server == NULL */
tv_ms_add(&asession_temp->expire, &now, t->be->appsession_timeout);
tv_add(&asession_temp->expire, &now, &t->be->appsession_timeout);
}/* end if ((t->proxy->appsession_name != NULL) ... */
}
@ -4835,7 +4837,7 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
if (asession_temp->serverid[0] == '\0')
memcpy(asession_temp->serverid, t->srv->id, server_id_len);
tv_ms_add(&asession_temp->expire, &now, t->be->appsession_timeout);
tv_add(&asession_temp->expire, &now, &t->be->appsession_timeout);
#if defined(DEBUG_HASH)
print_table(&(t->be->htbl_proxy));
@ -4997,7 +4999,7 @@ void get_srv_from_appsession(struct session *t, const char *begin, int len)
pool_free_to(apools.sessid, local_asession.sessid);
}
tv_ms_add(&asession_temp->expire, &now, t->be->appsession_timeout);
tv_add(&asession_temp->expire, &now, &t->be->appsession_timeout);
asession_temp->request_count++;
#if defined(DEBUG_HASH)

View File

@ -1,7 +1,7 @@
/*
* Proxy variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -170,17 +170,16 @@ int start_proxies(int verbose)
/*
* this function enables proxies when there are enough free sessions,
* or stops them when the table is full. It is designed to be called from the
* select_loop(). It returns the time left before next expiration event
* during stop time, TIME_ETERNITY otherwise.
* select_loop(). It returns the date of next expiration event during stop
* time, ETERNITY otherwise.
*/
int maintain_proxies(void)
void maintain_proxies(struct timeval *next)
{
struct proxy *p;
struct listener *l;
int tleft; /* time left */
p = proxy;
tleft = TIME_ETERNITY; /* infinite time */
tv_eternity(next);
/* if there are enough free sessions, we'll activate proxies */
if (actconn < global.maxconn) {
@ -233,13 +232,13 @@ int maintain_proxies(void)
p->state = PR_STSTOPPED;
}
else {
tleft = MINTIME(t, tleft);
tv_bound(next, &p->stop_time);
}
}
p = p->next;
}
}
return tleft;
return;
}

View File

@ -45,9 +45,9 @@ unsigned int srv_dynamic_maxconn(const struct server *s)
/*
* Manages a server's connection queue. If woken up, will try to dequeue as
* many pending sessions as possible, and wake them up. The task has nothing
* else to do, so it always returns TIME_ETERNITY.
* else to do, so it always returns ETERNITY.
*/
int process_srv_queue(struct task *t)
void process_srv_queue(struct task *t, struct timeval *next)
{
struct server *s = (struct server*)t->context;
struct proxy *p = s->proxy;
@ -65,7 +65,7 @@ int process_srv_queue(struct task *t)
task_wakeup(sess->task);
}
return TIME_ETERNITY;
tv_eternity(next);
}
/* Detaches the next pending connection from either a server or a proxy, and

View File

@ -165,8 +165,8 @@ int stream_sock_read(int fd) {
*/
if (b->flags & BF_PARTIAL_READ) {
if (b->rto) {
tv_ms_add(&b->rex, &now, b->rto);
if (tv_isset(&b->rto)) {
tv_add(&b->rex, &now, &b->rto);
goto out_wakeup;
}
out_eternity:
@ -315,8 +315,8 @@ int stream_sock_write(int fd) {
*/
if (b->flags & BF_PARTIAL_WRITE) {
if (b->wto) {
tv_ms_add(&b->wex, &now, b->wto);
if (tv_isset(&b->wto)) {
tv_add(&b->wex, &now, &b->wto);
/* FIXME: to prevent the client from expiring read timeouts during writes,
* we refresh it. A solution would be to merge read+write timeouts into a
* unique one, although that needs some study particularly on full-duplex

View File

@ -15,6 +15,7 @@
#include <common/standard.h>
#include <common/time.h>
#include <proto/proxy.h>
#include <proto/task.h>
#include <types/task.h>
@ -23,9 +24,6 @@
#include <import/tree.h>
/* FIXME : this should be removed very quickly ! */
extern int maintain_proxies(void);
void **pool_task= NULL;
void **pool_tree64 = NULL;
static struct ultree *stack[LLONGBITS];
@ -47,6 +45,7 @@ struct task *_task_wakeup(struct task *t)
{
return __task_wakeup(t);
}
/*
* task_queue()
*
@ -80,19 +79,15 @@ struct task *task_queue(struct task *task)
/*
* Extract all expired timers from the wait queue, and wakes up all
* associated tasks.
* Returns the time to wait for next task (next_time).
*
* FIXME: Use an alternative queue for ETERNITY tasks.
* associated tasks. Returns the date of next event (or eternity).
*
*/
int wake_expired_tasks()
void wake_expired_tasks(struct timeval *next)
{
__label__ out;
int slen;
struct task *task;
void *data;
int next_time;
/*
* Hint: tasks are *rarely* expired. So we can try to optimize
@ -102,19 +97,19 @@ int wake_expired_tasks()
if (likely(timer_wq.data != NULL)) {
task = LIST_ELEM(timer_wq.data, struct task *, qlist);
if (likely(__tv_isge(&task->expire, &now) > 0)) {
next_time = tv_ms_remain(&now, &task->expire);
__tv_remain(&now, &task->expire, next);
goto out;
}
}
/* OK we lose. Let's scan the tree then. */
next_time = TIME_ETERNITY;
tv_eternity(next);
tree64_foreach(&timer_wq, data, stack, slen) {
task = LIST_ELEM(data, struct task *, qlist);
if (__tv_isgt(&task->expire, &now)) {
next_time = tv_ms_remain(&now, &task->expire);
__tv_remain2(&now, &task->expire, next);
break;
}
@ -131,10 +126,7 @@ int wake_expired_tasks()
}
}
out:
/* Ensure that we don't report sub-millisecond timeouts */
if (next_time != TIME_ETERNITY)
next_time++;
return next_time;
return;
}
/*
@ -142,17 +134,16 @@ int wake_expired_tasks()
* - wake up all expired tasks
* - call all runnable tasks
* - call maintain_proxies() to enable/disable the listeners
* - return the delay till next event in ms, -1 = wait indefinitely
* - return the date of next event in <next> or eternity.
*
*/
int process_runnable_tasks()
void process_runnable_tasks(struct timeval *next)
{
int next_time;
int time2;
struct timeval temp;
struct task *t;
void *queue;
next_time = wake_expired_tasks();
wake_expired_tasks(next);
/* process each task in the run queue now. Each task may be deleted
* since we only use the run queue's head. Note that any task can be
* woken up by any other task and it will be processed immediately
@ -161,21 +152,20 @@ int process_runnable_tasks()
queue = run_queue;
foreach_dlist_item(t, queue, struct task *, qlist) {
int temp_time;
DLIST_DEL(&t->qlist);
t->qlist.p = NULL;
t->state = TASK_IDLE;
temp_time = t->process(t);
next_time = MINTIME(temp_time, next_time);
t->process(t, &temp);
tv_bound(next, &temp);
}
/* maintain all proxies in a consistent state. This should quickly
* become a task because it becomes expensive when there are huge
* numbers of proxies. */
time2 = maintain_proxies();
return MINTIME(time2, next_time);
maintain_proxies(&temp);
tv_bound(next, &temp);
return;
}
/*

View File

@ -94,6 +94,33 @@ REGPRM2 unsigned long _tv_ms_elapsed(const struct timeval *tv1, const struct tim
return __tv_ms_elapsed(tv1, tv2);
}
/*
* adds <inc> to <from>, set the result to <tv> and returns a pointer <tv>
*/
REGPRM3 struct timeval *_tv_add(struct timeval *tv, const struct timeval *from, const struct timeval *inc)
{
return __tv_add(tv, from, inc);
}
/*
* Computes the remaining time between tv1=now and event=tv2. if tv2 is passed,
* 0 is returned. The result is stored into tv.
*/
REGPRM3 struct timeval *_tv_remain(const struct timeval *tv1, const struct timeval *tv2, struct timeval *tv)
{
return __tv_remain(tv1, tv2, tv);
}
/*
* Computes the remaining time between tv1=now and event=tv2. if tv2 is passed,
* 0 is returned. The result is stored into tv. Returns ETERNITY if tv2 is
* eternity.
*/
REGPRM3 struct timeval *_tv_remain2(const struct timeval *tv1, const struct timeval *tv2, struct timeval *tv)
{
return __tv_remain2(tv1, tv2, tv);
}
/*
* Local variables:
* c-indent-level: 8