Merge branch 'ss/cleanup-idle' into next

This commit is contained in:
Nicolas Sebrecht 2011-09-12 20:02:26 +02:00
commit 78a67ac73c

View File

@ -458,9 +458,12 @@ class IMAPServer:
class IdleThread(object): class IdleThread(object):
def __init__(self, parent, folder=None): def __init__(self, parent, folder=None):
"""If invoked without 'folder', perform a NOOP and wait for
self.stop() to be called. If invoked with folder, switch to IDLE
mode and synchronize once we have a new message"""
self.parent = parent self.parent = parent
self.folder = folder self.folder = folder
self.event = Event() self.stop_sig = Event()
self.ui = getglobalui() self.ui = getglobalui()
if folder is None: if folder is None:
self.thread = Thread(target=self.noop) self.thread = Thread(target=self.noop)
@ -472,7 +475,7 @@ class IdleThread(object):
self.thread.start() self.thread.start()
def stop(self): def stop(self):
self.event.set() self.stop_sig.set()
def join(self): def join(self):
self.thread.join() self.thread.join()
@ -480,7 +483,7 @@ class IdleThread(object):
def noop(self): def noop(self):
imapobj = self.parent.acquireconnection() imapobj = self.parent.acquireconnection()
imapobj.noop() imapobj.noop()
self.event.wait() self.stop_sig.wait()
self.parent.releaseconnection(imapobj) self.parent.releaseconnection(imapobj)
def dosync(self): def dosync(self):
@ -495,21 +498,22 @@ class IdleThread(object):
ui.unregisterthread(currentThread()) ui.unregisterthread(currentThread())
def idle(self): def idle(self):
while True: """Invoke IDLE mode until timeout or self.stop() is invoked"""
if self.event.isSet():
return
self.needsync = False
self.imapaborted = False
def callback(args): def callback(args):
"""IDLE callback function invoked by imaplib2
This is invoked when a) The IMAP server tells us something
while in IDLE mode, b) we get an Exception (e.g. on dropped
connections, or c) the standard imaplib IDLE timeout of 29
minutes kicks in."""
result, cb_arg, exc_data = args result, cb_arg, exc_data = args
if exc_data is None: if exc_data is None and not self.stop_sig.isSet():
if not self.event.isSet(): # No Exception, and we are not supposed to stop:
self.needsync = True self.needsync = True
self.event.set() self.stop_sig.set() # continue to sync
else:
# We got an "abort" signal. while not self.stop_sig.isSet():
self.imapaborted = True self.needsync = False
self.stop()
success = False # successfully selected FOLDER? success = False # successfully selected FOLDER?
while not success: while not success:
@ -520,7 +524,7 @@ class IdleThread(object):
if e.severity == OfflineImapError.ERROR.FOLDER_RETRY: if e.severity == OfflineImapError.ERROR.FOLDER_RETRY:
# Connection closed, release connection and retry # Connection closed, release connection and retry
self.ui.error(e, exc_info()[2]) self.ui.error(e, exc_info()[2])
self.parent.releaseconnection(imapobj) self.parent.releaseconnection(imapobj, True)
else: else:
raise e raise e
else: else:
@ -528,19 +532,22 @@ class IdleThread(object):
if "IDLE" in imapobj.capabilities: if "IDLE" in imapobj.capabilities:
imapobj.idle(callback=callback) imapobj.idle(callback=callback)
else: else:
self.ui.warn("IMAP IDLE not supported on connection to %s." self.ui.warn("IMAP IDLE not supported on server '%s'."
"Falling back to old behavior: sleeping until next" "Sleep until next refresh cycle." % imapobj.identifier)
"refresh cycle."
%(imapobj.identifier,))
imapobj.noop() imapobj.noop()
self.event.wait() self.stop_sig.wait() # self.stop() or IDLE callback are invoked
if self.event.isSet(): try:
# Can't NOOP on a bad connection. # End IDLE mode with noop, imapobj can point to a dropped conn.
if not self.imapaborted:
imapobj.noop() imapobj.noop()
# We don't do event.clear() so that we'll fall out except imapobj.abort():
# of the loop next time around. self.ui.warn('Attempting NOOP on dropped connection %s' % \
imapobj.identifier)
self.parent.releaseconnection(imapobj, True)
else:
self.parent.releaseconnection(imapobj) self.parent.releaseconnection(imapobj)
if self.needsync: if self.needsync:
self.event.clear() # here not via self.stop, but because IDLE responded. Do
# another round and invoke actual syncing.
self.stop_sig.clear()
self.dosync() self.dosync()