hillz hillz - 14 days ago 4
Python Question

Python - How to properly set timeout of a function

I have 2 functions called

on_outbounddata()
and
on_execute
I want these functions to have timeout of 3 seconds, if they take longer than 3 seconds then exit the functions and continue running the other ones. Here's my code:

import socket,select,time,base64,os,sys,re,datetime, signal

class TheServer:
input_list = []
channel = {}
channel_ = {}
request = {}

def handler(self, signum, frame):
print "Time is up (3 sec)"
raise Exception("end of time")

def main_loop(self):
self.input_list.append(self.server)
while 1:
ss = select.select
inputready, outputready, exceptready = ss(self.input_list, [], [])
for self.s in inputready:
if self.s == self.server:
self.on_accept()
break
try:
self.netdata = self.s.recv(buffer_size)
except Exception, e:
self.netdata =''
if len(self.netdata) == 0:
self.on_close()
else:
signal.signal(signal.SIGALRM, self.handler)
signal.alarm(3)
try:
if cmp(self.channel[self.s],self.channel_[self.s]):
self.on_outbounddata() # I want this function to have a timeout of 3 seconds
else:
self.on_execute() # I want this function to have a timeout of 3 seconds
except Exception, exc:
print exc

def on_execute(self):
print "ON_EXECUTE"
netdata = self.netdata
#-----------------------------------------------------------
if netdata.find("CONNECT") ==0:
req="CONNECT " + host + ":" + port
payloads=payload
payloads=payloads.replace('^request^',req)
ms = re.search(r"\^s(\d+)\^", payloads)
if ms:
pay=payloads.split('^s'+ms.group(1)+'^')
self.request[self.channel[self.s]]=pay[1];
netdata=pay[0]
else:
netdata=payloads
#print netdata
try:
self.channel[self.s].send(netdata)
except Exception, e:
print e
#-----------------------------------------------------------
def on_outbounddata(self):
print "ON_OUTBOUNDDATA"
netdata = self.netdata
if netdata.find('HTTP/1.') ==0:
ms = re.search(r"\^s(\d+)\^", payload)
if ms:
print "Sleeping for " + ms.group(1) + "ms"
dec = int(ms.group(1)) / float(1000)
time.sleep(dec)
print self.request[self.s]
try:
self.channel_[self.s].send(self.request[self.s])
self.request[self.s]=''
except ValueError:
print "self.s is not in the list (on_outbounddata)"
pass
netdata='HTTP/1.1 200 Connection established\r\n\r\n'
try:
self.channel[self.s].send(netdata)
except Exception, e:
print e
except:
pass


Please note that I want to apply timeout only to
on_outbounddata()
and
on_execute
. When I run that code, instead of continuing to run the other functions it just breaks the while loop. How can I fix that ?

This is the error output:

ON_EXECUTE
ON_EXECUTE
ON_OUTBOUNDDATA
ON_OUTBOUNDDATA
ON_CLOSE
ON_CLOSE
Time is up (3 sec)
Traceback (most recent call last):
File "/usr/bin/socks", line 278, in <module>
server.main_loop()
File "/usr/bin/socks", line 159, in main_loop
inputready, outputready, exceptready = ss(self.input_list, [], [])
File "/usr/bin/socks", line 152, in handler
raise Exception("end of time")
Exception: end of time

Answer

Your issue is because you're not unsetting the alarm afterwards, so it continues and 3 seconds later it triggers, even if you're no longer in the section you wanted to timeout. The documentation explains how to do this:

signal.alarm(time) If time is non-zero, this function requests that a SIGALRM signal be sent to the process in time seconds. Any previously scheduled alarm is canceled (only one alarm can be scheduled at any time). The returned value is then the number of seconds before any previously set alarm was to have been delivered. If time is zero, no alarm is scheduled, and any scheduled alarm is canceled. If the return value is zero, no alarm is currently scheduled. (See the Unix man page alarm(2).) Availability: Unix.

Comments