[freetds] cancel and timeout

ZIGLIO, Frediano, VF-IT Frediano.Ziglio at vodafone.com
Fri Jan 21 06:02:13 EST 2005


> 
> On Thu, 20 Jan 2005 17:11:12 -0000, "Thompson, Bill D (London)"
> <bill_d_thompson at ml.com> wrote:
> > > Timeout
> > > -------
> > > Now if a timeout (now query_timeout) a query_timeout_func 
> it's called.
> > I
> > > removed chkintr and hndlintr (one function it's sufficient).
> > > query_start_time it's use for global time counting. I 
> don't know if
> > > query timeout on dblib works as expected. Perhaps even SQLFetch or
> > > similar need setting timeout.
> > 
> > I think there are two functions because this then mirrors 
> the dblibrary
> > interrupt handlers.  
> 
> Thanks for tackling this, Freddy.  
> 
> I think Bill means:
> 
> 	void dbsetinterrupt(dbproc, chkintr, hndlintr)
> 
> 	DBPROCESS     *dbproc;
> 	int           (*chkintr)();
> 	int           (*hndlintr)();
> 
> http://sybooks.sybase.com/onlinebooks/group-cnarc/cng1000e/dbl
> ib/@Generic__BookTextView/34848;pt=34848;uf=0#X
> 

I know. As usual I sum up too much.

How is timeout handled now in 0.64?

There are 4 fields in TDSSOCKET that deal with timeout
- query_timeout specify the timeout in seconds of query
- query_timeout_func specify the function to be colled when a timeout or
a interruption occur. This function now return the same information
returned by hndrintr (TDS_INT_CONTINUE, TDS_INT_CANCEL and so on)
- query_timeout_param specify the parameter to pass to
query_timeout_func. Perhaps this parameter can be replaced with
TDSSOCKET* and peraps it would be fine to pass time passed since start
- query_start_time specify the time the query has been sent to server.
If zero time it's computed from start of goodread.

A question I have it's: what should happen if query_timeout_func returns
TDS_INT_CONTINUE? Currently select receive a timeout of 1 second so it
call query_timeout_func every 1 second. query_timeout_func can set
query_timeout to 0 to disable this behavior or increase it (but not
knowing current time since start it's difficult to predict what should
be next timeout). Similarly, what should happen if query_timeout_func
returns TDS_INT_CANCEL? goodread should still call query_timeout_func?
Currently it does.

How to handle dblib way?
Some old email state that the polling it's every one seconds. To emulate
such behavior simply set query_timeout to 1 and register a
query_timeout_func. Note that this function it's called even if an
interruption it's catched. We can change time resolution (if needed)
using second tenths instead of seconds (to be honest I hate polling if
not necessary it's a waste of cpu and memory).

I think that part of this explanation (reviewed) should go to doxygen
docs too...

> 
> Note the comment:
> 
> "DB-Library does non-blocking reads from the server. While 
> waiting for a
> read from the server, it will call the chkintr() function to see if an
> interrupt is pending. If chkintr() returns TRUE and a handler has been
> installed as the hndlintr() for dbsetinterrupt, hndlintr() will be
> called."
> 
> That's not how dblib_query_timeout() is currently coded.  
> chkintr should
> be called periodically from within src/tds/net.c::goodread() 
> until either:
> 
> 1.  the server sends data, or
> 2.  the query_timeout expires.
> 
> goodread() calls hndlintr after the timeout.  It should call it while
> counting down to the timeout.  How often?  The docs don't 
> say.  They say
> they (Sybase) do non-blocking I/O, implying a busy wait.  I 
> think it would
> be OK to call select(2) with a timeout of, say, 100 ms 
> (blocking), then
> call hndlintr, then busyfunc, then deduct 100 ms from 
> query_timeout, and
> call select(2) again.  Something like:
> 
> 	for (ti = selecttimeout; 
> 		retcode = select(tds->s + 1, &fds, NULL, NULL, ti);
> 		ti.tv_usec -= 100) 
> 	{
> 		if (retcode < 0) {
> 			/* error */
> 		}
> 		if (retcode > 0) {
> 			break; /* data ready: read */
> 		}
> 		assert(retcode == 0); /* partial timeout */
> 		if (ti.tv_usec < 0) {
> 			ti.tv_sec--;
> 			ti.tv_usec += 1000;
> 		}
> 
> 		if (ti.tv_sec < 0) break; /* really timed out */
> 
> 		if ((*tds->chkintr)())
> 			(*tds->hndlintr());
> 		if (tds->busyfunc)
> 			(*tds->busyfunc)();
> 	}
> 	
> 	len = READSOCKET(tds->s, buf + got, buflen);
> 		
> 	if (tds->idlefunc)
> 		(*tds->idlefunc)();
> 	

>From documentation busyfunc should be called before reading any part of
packet and idlefunc should be called when all packet is read. This part
of code can call busy/idle in the middle of packet. Perhaps it would be
better to define a goodread that accept am additional timeout returns
something like:
- bytes readed
- timeout error (ie -1)
- interrupt error (ie -2)
- other errors (ie -3)
just to separate code that handle packet/timeout (I think I put too much
code in net.c)...
I don't like also that socket it's blocking/no-blocking depending on
connect_timeout.. IMHO should be always no-blocking (less code to debug
and mantain and fixes some possible race condition that can lead to
blocking).

> 
> These may also be interesting:
> 
> dbsetidle:
> http://sybooks.sybase.com/onlinebooks/group-cnarc/cng1000e/dbl
> ib/@Generic__BookTextView/34650;pt=3564
> 
> "idlefunc() will be automatically called whenever DB-Library 
> has finished
> reading output from the server."
> 
> dbsetbusy:
> http://sybooks.sybase.com/onlinebooks/group-cnarc/cng1000e/dbl
> ib/@Generic__BookTextView/34263;pt=34263;uf=0#X
> 
> "automatically called whenever DB-Library is reading or 
> waiting to read
> output from the server."
> 

freddy77


More information about the FreeTDS mailing list