Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Time out question

5 views
Skip to first unread message

DarkBlue

unread,
Jul 2, 2006, 8:47:17 PM7/2/06
to
My application makes several connections to
a remote database server via tcp/ip.
Usually all is fine,but occasionally the server is
down or the internet does not work and then there is
the 30 sec to several minutes timeout wait for the
tcp to give up.
Is there anything I can do without using
threads,sockets,twisted etc. to have following :

pseudocode :

try for 10 seconds
if database.connected :
do your remote thing
except raise after 10 seconds
abort any connection attempt
do something else


Thanks

Alex Martelli

unread,
Jul 2, 2006, 8:57:23 PM7/2/06
to
DarkBlue <nom...@nixmail.com> wrote:

Sure, see function setdefaulttimeout in module socket. Just call it as
you wish before trying the DB connection and catch the resulting
exception.


Alex

DarkBlue

unread,
Jul 3, 2006, 7:36:06 AM7/3/06
to

>
> Sure, see function setdefaulttimeout in module socket. Just call it as
> you wish before trying the DB connection and catch the resulting
> exception.
>
>
> Alex

That should do it.

Thanks
Db

Nick Craig-Wood

unread,
Jul 3, 2006, 8:30:04 AM7/3/06
to
Alex Martelli <al...@mac.com> wrote:

> DarkBlue <nom...@nixmail.com> wrote:
> > try for 10 seconds
> > if database.connected :
> > do your remote thing
> > except raise after 10 seconds
> > abort any connection attempt
> > do something else
>
> Sure, see function setdefaulttimeout in module socket. Just call it as
> you wish before trying the DB connection and catch the resulting
> exception.

It would be nice to have language support for the general case, ie a
general purpose timeout.

In python 2.5 wouldn't it be nice to write

with timeout(seconds=10) as time_exceeded:
do potentially time consuming stuff not necessarily involving sockets
if time_exceeded:
report an error or whatever

Here is some code I came up with to implement this idea for
python2.3+. It should be cross platform but I only tested it on
linux. I think there may still be a threading bug in there (see
FIXME), but it seems to work. It requires ctypes for access to
PyThreadState_SetAsyncExc).

It is mostly test code as it took absolutely ages to debug! Threading
code is hard ;-)

............................................................

"""
General purpose timeout mechanism not using alarm(), ie cross platform

Eg

from timeout import Timeout, TimeoutError

def might_infinite_loop(arg):
while 1:
pass

try:
Timeout(10, might_infinite_loop, "some arg")
except TimeoutError:
print "Oops took too long"
else:
print "Ran just fine"

"""

import threading
import time
import sys
import ctypes
import os

class TimeoutError(Exception):
"""Thrown on a timeout"""
PyThreadState_SetAsyncExc = ctypes.pythonapi.PyThreadState_SetAsyncExc
_c_TimeoutError = ctypes.py_object(TimeoutError)

class Timeout(threading.Thread):
"""
A General purpose timeout class
timeout is int/float in seconds
action is a callable
*args, **kwargs are passed to the callable
"""
def __init__(self, timeout, action, *args, **kwargs):
threading.Thread.__init__(self)
self.action = action
self.args = args
self.kwargs = kwargs
self.stopped = False
self.exc_value = None
self.end_lock = threading.Lock()
# start subtask
self.setDaemon(True) # FIXME this shouldn't be needed but is, indicating sub tasks aren't ending
self.start()
# Wait for subtask to end naturally
self.join(timeout)
# Use end_lock to kill the thread in a non-racy
# fashion. (Using isAlive is racy). Poking exceptions into
# the Thread cleanup code isn't a good idea either
if self.end_lock.acquire(False):
# gained end_lock => sub thread is still running
# sub thread is still running so kill it with a TimeoutError
self.exc_value = TimeoutError()
PyThreadState_SetAsyncExc(self.id, _c_TimeoutError)
# release the lock so it can progress into thread cleanup
self.end_lock.release()
# shouldn't block since we've killed the thread
self.join()
# re-raise any exception
if self.exc_value:
raise self.exc_value
def run(self):
self.id = threading._get_ident()
try:
self.action(*self.args, **self.kwargs)
except:
self.exc_value = sys.exc_value
# only end if we can acquire the end_lock
self.end_lock.acquire()

if __name__ == "__main__":

def _spin(t):
"""Spins for t seconds"""
start = time.time()
end = start + t
while time.time() < end:
pass

def _test_time_limit(name, expecting_time_out, t_limit, fn, *args, **kwargs):
"""Test Timeout"""
start = time.time()

if expecting_time_out:
print "Test",name,"should timeout"
else:
print "Test",name,"shouldn't timeout"

try:
Timeout(t_limit, fn, *args, **kwargs)
except TimeoutError, e:
if expecting_time_out:
print "Timeout generated OK"
else:
raise RuntimeError("Wasn't expecting TimeoutError Here")
else:
if expecting_time_out:
raise RuntimeError("Was expecting TimeoutError Here")
else:
print "No TimeoutError generated OK"

elapsed = time.time() - start
print "That took",elapsed,"seconds for timeout of",t_limit

def test():
"""Test code"""

# NB the commented out bits of code don't work with this type
# of timeout nesting.

# no nesting
_test_time_limit("simple #1", True, 5, _spin, 10)
_test_time_limit("simple #2", False, 10, _spin, 5)

# 1 level of nesting
_test_time_limit("nested #1", True, 4, _test_time_limit,
"nested #1a", True, 5, _spin, 10)
_test_time_limit("nested #2", False, 6, _test_time_limit,
"nested #2a", True, 5, _spin, 10)
#_test_time_limit("nested #3", True, 4, _test_time_limit,
# "nested #3a", False, 10, _spin, 5)
_test_time_limit("nested #4", False, 6, _test_time_limit,
"nested #4a", False, 10, _spin, 5)

# 2 level of nesting
_test_time_limit("nested #5", True, 3, _test_time_limit,
"nested #5a", True, 4, _test_time_limit,
"nested #5b", True, 5, _spin, 10)
#_test_time_limit("nested #6", True, 3, _test_time_limit,
# "nested #6a", False, 6, _test_time_limit,
# "nested #6b", True, 5, _spin, 10)
#_test_time_limit("nested #7", True, 3, _test_time_limit,
# "nested #7a", True, 4, _test_time_limit,
# "nested #7b", False, 10, _spin, 5)
#_test_time_limit("nested #8", True, 3, _test_time_limit,
# "nested #8a", False, 6, _test_time_limit,
# "nested #8b", False, 10, _spin, 5)
_test_time_limit("nested #9", False, 7, _test_time_limit,
"nested #9a", True, 4, _test_time_limit,
"nested #9b", True, 5, _spin, 10)
_test_time_limit("nested #10", False, 7, _test_time_limit,
"nested #10a",False, 6, _test_time_limit,
"nested #10b",True, 5, _spin, 10)
#_test_time_limit("nested #11", False, 7, _test_time_limit,
# "nested #11a",True, 4, _test_time_limit,
# "nested #11b",False, 10, _spin, 5)
_test_time_limit("nested #12", False, 7, _test_time_limit,
"nested #12a",False, 6, _test_time_limit,
"nested #12b",False, 10, _spin, 5)

print "All tests OK"

test()


--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick

Grant Edwards

unread,
Jul 3, 2006, 10:30:10 AM7/3/06
to
On 2006-07-03, Nick Craig-Wood <ni...@craig-wood.com> wrote:
> Alex Martelli <al...@mac.com> wrote:
>> DarkBlue <nom...@nixmail.com> wrote:
>> > try for 10 seconds
>> > if database.connected :
>> > do your remote thing
>> > except raise after 10 seconds
>> > abort any connection attempt
>> > do something else
>>
>> Sure, see function setdefaulttimeout in module socket. Just call it as
>> you wish before trying the DB connection and catch the resulting
>> exception.
>
> It would be nice to have language support for the general case, ie a
> general purpose timeout.

I just use signal.alarm():

import signal,sys

def alarmHandler(signum, frame):
raise 'Timeout'

signal.signal(signal.SIGALRM, alarmHandler)

while 1:
try:
signal.alarm(5)
t = sys.stdin.readline()
signal.alarm(0)
print t
except 'Timeout':
print "too slow"

--
Grant Edwards grante Yow! I will SHAVE and
at buy JELL-O and bring my
visi.com MARRIAGE MANUAL!!

Rune Strand

unread,
Jul 3, 2006, 11:09:50 AM7/3/06
to

Grant Edwards wrote:
> I just use signal.alarm():
>
> import signal,sys
>
> def alarmHandler(signum, frame):
> raise 'Timeout'
>
> signal.signal(signal.SIGALRM, alarmHandler)
>
> while 1:
> try:
> signal.alarm(5)
> t = sys.stdin.readline()
> signal.alarm(0)
> print t
> except 'Timeout':
> print "too slow"
>
> --

Very nice, but UNIX only.

Nick Craig-Wood

unread,
Jul 4, 2006, 5:30:03 AM7/4/06
to
Grant Edwards <gra...@visi.com> wrote:
> On 2006-07-03, Nick Craig-Wood <ni...@craig-wood.com> wrote:
> > Alex Martelli <al...@mac.com> wrote:
> >> DarkBlue <nom...@nixmail.com> wrote:
> >> > try for 10 seconds
> >> > if database.connected :
> >> > do your remote thing
> >> > except raise after 10 seconds
> >> > abort any connection attempt
> >> > do something else
> >>
> >> Sure, see function setdefaulttimeout in module socket. Just call it as
> >> you wish before trying the DB connection and catch the resulting
> >> exception.
> >
> > It would be nice to have language support for the general case, ie a
> > general purpose timeout.
>
> I just use signal.alarm():

There are quite a lot of problems with alarm()

1) doesn't work on windows
2) there is only one alarm so nested alarms are tricky
3) alarm() only has 1 second resolution, however you can use the POSIX
setitimer() which has better resolution
4) signals make havoc with some things (signals are basically
interrupts passed to user space under unix)
5) alarm() doesn't play nice with threads

All that said it does work under unix for a lot of programs. Here is
a module which fixes point 2) anyway...

It is a lot easier to follow than the threads example I posted
earlier. There are still potential race conditions though even with
something as simple as alarm()!

............................................................

"""
Implement time limited function calls. Only works on platforms which
support the signal module and signal.alarm()

Eg

from time_limit import time_limit, TimeOut

def might_infinite_loop(arg):
while 1:
pass

try:
time_limit(10, might_infinite_loop, "some arg")
except TimeOut:


print "Oops took too long"
else:
print "Ran just fine"

"""

import signal
import time
import inspect

class TimeOut(Exception):
"""Thrown on alarm"""
pass

class _NestedTimeOut(Exception):
"""Thrown on TimeOut detected for next layer"""
pass

def _sig_alarm(signum, frame):
"""Alarm handler for this module"""
raise TimeOut()

def time_limit(t_limit, fn, *args, **kwargs):
"""
Calls fn with the *args and **kwargs returning its result or
raising a TimeOut exception if it doesn't complete within t
seconds
"""

# Turn alarm off and read old value
old_t_limit = signal.alarm(0)
start_time = time.time()
# Install new handler remembering old
old_handler = signal.signal(signal.SIGALRM, _sig_alarm)
# Set the timer going
signal.alarm(t_limit)

try:
try:
try:
rc = fn(*args, **kwargs)
except _NestedTimeOut:
raise TimeOut()
finally:
# Disable alarm
signal.alarm(0)
finally:
# Alarm will be disabled or will have gone off when we get here
# Restore the old handler
signal.signal(signal.SIGALRM, old_handler)
# If there was an alarm running restore it
if old_t_limit > 0:
# Calculate how much of the old timeout is left
elapsed_time = time.time() - start_time
old_t_limit = int(round(old_t_limit - elapsed_time, 0))
if old_t_limit <= 0:
# Alarm should have gone off so call old signal handler
if old_handler == _sig_alarm:
# If handler is our handler then...
raise _NestedTimeOut()
else:
# Call old handler
old_handler(signal.SIGALRM, inspect.currentframe())
else:
# Re-enable alarm
signal.alarm(old_t_limit)

return rc

if __name__ == "__main__":

def _spin(t):
"""Spins for t seconds"""
start = time.time()
end = start + t
while time.time() < end:
pass

def _test_time_limit(name, expecting_time_out, t_limit, fn, *args, **kwargs):

"""Test time_limit"""


start = time.time()

if expecting_time_out:
print "Test",name,"should timeout"
else:
print "Test",name,"shouldn't timeout"

try:
time_limit(t_limit, fn, *args, **kwargs)
except TimeOut, e:


if expecting_time_out:
print "Timeout generated OK"
else:

raise RuntimeError("Wasn't expecting TimeOut Here")
else:
if expecting_time_out:
raise RuntimeError("Was expecting TimeOut Here")
else:
print "No timeout generated OK"

elapsed = time.time() - start
print "That took",elapsed,"seconds for timeout of",t_limit

def test():
"""Test code"""

# no nesting


_test_time_limit("simple #1", True, 5, _spin, 10)
_test_time_limit("simple #2", False, 10, _spin, 5)

# 1 level of nesting
_test_time_limit("nested #1", True, 4, _test_time_limit, "nested #1a", True, 5, _spin, 10)
_test_time_limit("nested #2", False, 6, _test_time_limit, "nested #2a", True, 5, _spin, 10)

_test_time_limit("nested #3", True, 4, _test_time_limit, "nested #3a", False, 10, _spin, 5)


_test_time_limit("nested #4", False, 6, _test_time_limit, "nested #4a", False, 10, _spin, 5)

# 2 level of nesting
_test_time_limit("nested #5", True, 3, _test_time_limit, "nested #5a", True, 4, _test_time_limit, "nested #5b", True, 5, _spin, 10)

_test_time_limit("nested #6", True, 3, _test_time_limit, "nested #6a", False, 6, _test_time_limit, "nested #6b", True, 5, _spin, 10)
_test_time_limit("nested #7", True, 3, _test_time_limit, "nested #7a", True, 4, _test_time_limit, "nested #7b", False, 10, _spin, 5)
_test_time_limit("nested #8", True, 3, _test_time_limit, "nested #8a", False, 6, _test_time_limit, "nested #8b", False, 10, _spin, 5)


_test_time_limit("nested #9", False, 7, _test_time_limit, "nested #9a", True, 4, _test_time_limit, "nested #9b", True, 5, _spin, 10)

_test_time_limit("nested #10", False, 7, _test_time_limit, "nested #10a", False, 6, _test_time_limit, "nested #10b", True, 5, _spin, 10)
_test_time_limit("nested #11", False, 7, _test_time_limit, "nested #11a", True, 4, _test_time_limit, "nested #11b", False, 10, _spin, 5)
_test_time_limit("nested #12", False, 7, _test_time_limit, "nested #12a", False, 6, _test_time_limit, "nested #12b", False, 10, _spin, 5)

0 new messages