asyncore.loop doesn't terminate when there are no more connections
Asked Answered
F

3

0

I am following some example code to use asyncore here, only having set a timeout value for asyncore.loop as in the following full example:

import smtpd
import asyncore

class CustomSMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
        print 'Receiving message from:', peer
        print 'Message addressed from:', mailfrom
        print 'Message addressed to  :', rcpttos
        print 'Message length        :', len(data)
        return

server = CustomSMTPServer(('127.0.0.1', 1025), None)

asyncore.loop(timeout = 1)

I have expected that a timeout occurs after 1 second, but this is not the case. The code runs much longer for than one second. What am I missing here?

Ftlb answered 23/1, 2013 at 17:18 Comment(7)
@pst: Count is not an option. I do not know if there will be zero or more than zero connections beforehand.Ftlb
Then stop beating around the bushConal
@pst I do not know what you mean. My question is: Why does the timeout for asyncore.loop() does not end the function asyncore.loop() after the specified time.Ftlb
Well, consider some cases: 1) the documentation is lying that it will stop running when there are no more open channels, 2) the documentation is lying about what the timeout does, 3) asyncore is not using the specified timeout with select 4) there are open channels .. I suspect it will end shortly if a count (say, 2) is specified. If that is indeed the case then #2 and #3 can likely be ruled out.Conal
@pst: I do not understand what you mean. Maybe you could start answering the following question in a clear, concise and well-phrased answer: What is the purpose of the timeout argument in asyncore.loop()?Ftlb
The documentation said what it does. Since you are making an assertion, I have provided some hypothesis to follow to verify or disprove said claim.Conal
let us continue this discussion in chatFtlb
F
1

I really do not know if the timeout argument to asyncore.loop() really is meant to timeout the function call asyncore.loop() after the specified time, but here is a receipt to make that function timeout after a specified time (replacing the line with asyncore.loop() in the example code):

import signal

class TimeoutError(Exception): pass

# define the timeout handler
def handler(signum, frame):
    raise TimeoutError()

# set the timeout handler and the signal duration
signal.signal(signal.SIGALRM, handler)
signal.alarm(1)
try:
    asyncore.loop()
except TimeoutError as exc:
    print "timeout"
finally:
    signal.alarm(0)
Ftlb answered 23/1, 2013 at 17:30 Comment(1)
Note: not available on Windows with Python 2.7Radarman
D
2

The timeout argument to asyncore.loop() is the amount of time the select.select call will wait for data. If there is no data before the timeout runs out it will loop and call select.select again.

Same for the channels idea. This does not mean open sockets but means active asyncore.dispatcher or asynchat.async_chat instances. If you want to stop the loop you will have to call the close() method on ALL instances registered.

In your case server.close() will close the instance/channel and remove it from the asyncore loop. If no more channels are active this loop will then terminate itself.

Delainedelainey answered 23/1, 2013 at 17:29 Comment(4)
How can I call a close method if asyncore.loop() blocks my code?Ftlb
@Ftlb You should either run the asyncore.loop() in a new thread or call the close inside one of the handlers. I don't know how you determine when to exit so if you tell us how you determine that we could help more.Delainedelainey
I have been trying to run asyncore.loop() in a thread, see #14483695, but I need to figure out where and how to call close or how and where to modify any hander method. If you know how, I really would appreciate if you could elaborate this in the other question.Ftlb
@Ftlb I posted an answer on the linked question. Please comment there if you need more help.Delainedelainey
F
1

I really do not know if the timeout argument to asyncore.loop() really is meant to timeout the function call asyncore.loop() after the specified time, but here is a receipt to make that function timeout after a specified time (replacing the line with asyncore.loop() in the example code):

import signal

class TimeoutError(Exception): pass

# define the timeout handler
def handler(signum, frame):
    raise TimeoutError()

# set the timeout handler and the signal duration
signal.signal(signal.SIGALRM, handler)
signal.alarm(1)
try:
    asyncore.loop()
except TimeoutError as exc:
    print "timeout"
finally:
    signal.alarm(0)
Ftlb answered 23/1, 2013 at 17:30 Comment(1)
Note: not available on Windows with Python 2.7Radarman
F
0

The timeout of asyncore.loop() is the timeout for select().

It is not useful, because when select() timeout, it loops back, see the pseudo code:

while True:
    do_something()
    select(...)
    do_something_else()

If I do simulation with firewall-ed sockets, in my Python 2.7.3 asyncore.loop() timeout 1 minute after no data is received from some socket.

I found very useful to have following method in asyncore.dispatcher "subclass":

def handle_error(self):
    raise

In this way I had "proper" exception dump.

Because I do not wanted to have exception, later I changed it to something like:

def handle_error(self):
    print "Error downloading %s" % self.host
    pass

Now my code works correct, without exception.

I did not found a way to control the timeout.

Freewheel answered 3/4, 2013 at 22:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.