Skip to Content.
Sympa Menu

freetds - RE: [freetds] cancel and timeout

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: "ZIGLIO, Frediano, VF-IT" <Frediano.Ziglio AT vodafone.com>
  • To: "FreeTDS Development Group" <freetds AT lists.ibiblio.org>
  • Subject: RE: [freetds] cancel and timeout
  • Date: Fri, 21 Jan 2005 12:02:13 +0100

>
> 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




Archive powered by MHonArc 2.6.24.

Top of Page