/head: changeset 60

Start of support for multi-threading
This commit is contained in:
jgoerzen 2002-07-03 14:04:40 +01:00
parent d7963492a7
commit b6897b7165
2 changed files with 94 additions and 15 deletions

View File

@ -17,9 +17,34 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from offlineimap import imaplib, imaputil from offlineimap import imaplib, imaputil
from threading import *
class UsefulIMAPMixIn:
def getstate(self):
return self.state
def getselectedfolder(self):
if self.getstate() == 'SELECTED':
return self.selectedfolder
return None
def select(self, mailbox='INBOX', readonly=None):
if self.getselectedfolder() == mailbox and not readonly:
# No change; return.
return
result = self.__class__.__bases__[1].select(self, mailbox, readonly)
if result[0] != 'OK':
raise ValueError, "Error from select: %s" % str(result)
if self.getstate() == 'SELECTED' and not readonly:
self.selectedfolder = mailbox
else:
self.selectedfolder = None
class UsefulIMAP4(UsefulIMAPMixIn, imaplib.IMAP4): pass
class UsefulIMAP4_SSL(UsefulIMAPMixIn, imaplib.IMAP4_SSL): pass
class IMAPServer: class IMAPServer:
def __init__(self, username, password, hostname, port = None, ssl = 1): def __init__(self, username, password, hostname, port = None, ssl = 1,
maxconnections = 1):
self.username = username self.username = username
self.password = password self.password = password
self.hostname = hostname self.hostname = hostname
@ -32,30 +57,57 @@ class IMAPServer:
self.port = 993 self.port = 993
else: else:
self.port = 143 self.port = 143
self.imapobj = None self.maxconnections = maxconnections
self.availableconnections = []
self.assignedconnections = []
self.semaphore = BoundedSemaphore(self.maxconnections)
self.connectionlock = Lock()
def getdelim(self): def getdelim(self):
"""Returns this server's folder delimiter. Can only be called """Returns this server's folder delimiter. Can only be called
after one or more calls to makeconnection.""" after one or more calls to acquireconnection."""
return self.delim return self.delim
def getroot(self): def getroot(self):
"""Returns this server's folder root. Can only be called after one """Returns this server's folder root. Can only be called after one
or more calls to makeconnection.""" or more calls to acquireconnection."""
return self.root return self.root
def makeconnection(self):
"""Opens a connection to the server and returns an appropriate def releaseconnection(self, connection):
self.connectionlock.acquire()
self.assignedconnections.remove(connection)
self.availableconnections.append(connection)
self.connectionlock.release()
self.semaphore.release()
def acquireconnection(self):
"""Fetches a connection from the pool, making sure to create a new one
if needed, to obey the maximum connection limits, etc.
Opens a connection to the server and returns an appropriate
object.""" object."""
if self.imapobj != None: self.semaphore.acquire()
return self.imapobj self.connectionlock.acquire()
imapobj = None imapobj = None
if self.usessl:
imapobj = imaplib.IMAP4_SSL(self.hostname, self.port)
else:
imapobj = imaplib.IMAP4(self.hostname, self.port)
if len(self.availableconnections): # One is available.
imapobj = self.availableconnections[0]
self.assignedconnections.append(imapobj)
del(self.availableconnections[0])
self.connectionlock.release()
return imapobj
self.connectionlock.release() # Release until need to modify data
# Generate a new connection.
if self.usessl:
imapobj = UsefulIMAP4_SSL(self.hostname, self.port)
else:
imapobj = UsefulIMAP4(self.hostname, self.port)
imapobj.login(self.username, self.password) imapobj.login(self.username, self.password)
if self.delim == None: if self.delim == None:
@ -64,9 +116,34 @@ class IMAPServer:
self.delim = imaputil.dequote(self.delim) self.delim = imaputil.dequote(self.delim)
self.root = imaputil.dequote(self.root) self.root = imaputil.dequote(self.root)
self.imapobj = imapobj self.connectionlock.acquire()
self.assignedconnections.append(imapobj)
self.connectionlock.release()
return imapobj return imapobj
def connectionwait(self):
"""Waits until there is a connection available. Note that between
the time that a connection becomes available and the time it is
requested, another thread may have grabbed it. This function is
mainly present as a way to avoid spawning thousands of threads
to copy messages, then have them all wait for 3 available connections.
It's OK if we have maxconnections + 1 or 2 threads, which is what
this will help us do."""
self.semaphore.acquire()
self.semaphore.release()
def close(self): def close(self):
self.imapobj.logout() # Make sure I own all the semaphores. Let the threads finish
self.imapobj = None # their stuff. This is a blocking method.
self.connectionlock.acquire()
for i in range(self.maxconnections):
self.semaphore.acquire()
for imapobj in self.assignedconnections + self.availableconnections:
imapobj.logout()
self.assignedconnections = []
self.availableconnections = []
for i in range(self.maxconnections):
self.semaphore.release()
self.connectionlock.release()

View File

@ -19,6 +19,7 @@
from Base import BaseRepository from Base import BaseRepository
from offlineimap import folder, imaputil from offlineimap import folder, imaputil
import re import re
from threading import *
class IMAPRepository(BaseRepository): class IMAPRepository(BaseRepository):
def __init__(self, config, accountname, imapserver): def __init__(self, config, accountname, imapserver):
@ -30,6 +31,7 @@ class IMAPRepository(BaseRepository):
self.imapobj = imapserver.makeconnection() self.imapobj = imapserver.makeconnection()
self.folders = None self.folders = None
self.nametrans = lambda foldername: foldername self.nametrans = lambda foldername: foldername
self.maxconnections = config.getint(accountname, 'maxconnections')
if config.has_option(accountname, 'nametrans'): if config.has_option(accountname, 'nametrans'):
self.nametrans = eval(config.get(accountname, 'nametrans')) self.nametrans = eval(config.get(accountname, 'nametrans'))