G
Giampaolo Rodola'
Hi,
I'm the maintainer of an asynchronous FTP server implementation based
on asyncore.
Some days ago I thought it would be interesting to add a class
offering the possibility to run the asyncore loop into a thread so
that a user can run the server without blocking the entire
application.
It could be useful, for example, in case someone wants to integrate a
GUI.
Since I'm not good with multi-threaded programming I'd like some
opinions about the code I'm going to paste below.
The FTPServer class that I inherited in my subclass is the
"dispatcher" which listens on port 21 dispatching the incoming
connection to an "handler".
The polling loop (FTPServer.serve_forever()) is wrapped in the run
method.
As you can see I acquire and release the lock (threading.Lock) every
time I call the polling loop.
My question is: is that really necessary?
Should I expect some weird behavior by running the main loop into a
thread like I did?
Thanks in advance
class ThreadedFTPServer(FTPServer, threading.Thread):
"""A modified version of the FTPServer class which wraps the
'serve_forever' polling loop into a thread.
The instance returned can be used to start(), stop() and
eventually re-start() the server.
"""
def __init__(self, address, handler):
FTPServer.__init__(self, address, handler)
threading.Thread.__init__(self)
self.__lock = threading.Lock()
self.__flag = threading.Event()
self.__serving = False
self.__stopped = False
def __repr__(self):
status = [self.__class__.__module__ + "." +
self.__class__.__name__]
if self.__serving:
status.append('active')
else:
status.append('inactive')
status.append('%s:%s' %self.socket.getsockname()[:2])
return '<%s at %#x>' % (' '.join(status), id(self))
def start(self, timeout=1, use_poll=False, map=None):
"""Start serving until an explicit stop() request.
Polls for shutdown every 'timeout' seconds.
"""
if self.__serving:
raise RuntimeError("Server already started")
if self.__stopped:
# ensure the server can be started again
ThreadedFTPServer.__init__(self, self.socket.getsockname
(),
self.handler)
self.__timeout = timeout
self.__use_poll = use_poll
self.__map = map
threading.Thread.start(self)
self.__flag.wait()
server_forever = start
def run(self):
self.__serving = True
self.__flag.set()
while self.__serving:
self.__lock.acquire()
FTPServer.serve_forever(self, timeout=self.__timeout,
count=1,
use_poll=self.__use_poll,
map=self.__map)
self.__lock.release()
FTPServer.close_all(self, ignore_all=True)
def stop(self):
"""Stop serving (also disconnecting all currently connected
clients) by telling the serve_forever() loop to stop and
waits until it does.
"""
if not self.__serving:
raise RuntimeError("Server not started yet")
self.__serving = False
self.__stopped = True
self.join()
close = close_all = stop
--- Giampaolo
http://code.google.com/p/pyftpdlib/
I'm the maintainer of an asynchronous FTP server implementation based
on asyncore.
Some days ago I thought it would be interesting to add a class
offering the possibility to run the asyncore loop into a thread so
that a user can run the server without blocking the entire
application.
It could be useful, for example, in case someone wants to integrate a
GUI.
Since I'm not good with multi-threaded programming I'd like some
opinions about the code I'm going to paste below.
The FTPServer class that I inherited in my subclass is the
"dispatcher" which listens on port 21 dispatching the incoming
connection to an "handler".
The polling loop (FTPServer.serve_forever()) is wrapped in the run
method.
As you can see I acquire and release the lock (threading.Lock) every
time I call the polling loop.
My question is: is that really necessary?
Should I expect some weird behavior by running the main loop into a
thread like I did?
Thanks in advance
class ThreadedFTPServer(FTPServer, threading.Thread):
"""A modified version of the FTPServer class which wraps the
'serve_forever' polling loop into a thread.
The instance returned can be used to start(), stop() and
eventually re-start() the server.
"""
def __init__(self, address, handler):
FTPServer.__init__(self, address, handler)
threading.Thread.__init__(self)
self.__lock = threading.Lock()
self.__flag = threading.Event()
self.__serving = False
self.__stopped = False
def __repr__(self):
status = [self.__class__.__module__ + "." +
self.__class__.__name__]
if self.__serving:
status.append('active')
else:
status.append('inactive')
status.append('%s:%s' %self.socket.getsockname()[:2])
return '<%s at %#x>' % (' '.join(status), id(self))
def start(self, timeout=1, use_poll=False, map=None):
"""Start serving until an explicit stop() request.
Polls for shutdown every 'timeout' seconds.
"""
if self.__serving:
raise RuntimeError("Server already started")
if self.__stopped:
# ensure the server can be started again
ThreadedFTPServer.__init__(self, self.socket.getsockname
(),
self.handler)
self.__timeout = timeout
self.__use_poll = use_poll
self.__map = map
threading.Thread.start(self)
self.__flag.wait()
server_forever = start
def run(self):
self.__serving = True
self.__flag.set()
while self.__serving:
self.__lock.acquire()
FTPServer.serve_forever(self, timeout=self.__timeout,
count=1,
use_poll=self.__use_poll,
map=self.__map)
self.__lock.release()
FTPServer.close_all(self, ignore_all=True)
def stop(self):
"""Stop serving (also disconnecting all currently connected
clients) by telling the serve_forever() loop to stop and
waits until it does.
"""
if not self.__serving:
raise RuntimeError("Server not started yet")
self.__serving = False
self.__stopped = True
self.join()
close = close_all = stop
--- Giampaolo
http://code.google.com/p/pyftpdlib/