diff --git a/offlineimap/init.py b/offlineimap/init.py index 1e2917e..9a6e959 100644 --- a/offlineimap/init.py +++ b/offlineimap/init.py @@ -25,7 +25,7 @@ import logging from optparse import OptionParser import offlineimap -from offlineimap import accounts, threadutil, syncmaster, folder +from offlineimap import accounts, threadutil, folder from offlineimap import globals from offlineimap.ui import UI_LIST, setglobalui, getglobalui from offlineimap.CustomConfig import CustomConfigParser @@ -36,6 +36,28 @@ import traceback import collections +def syncaccount(config, accountname): + """Return a new running thread for this account.""" + + account = accounts.SyncableAccount(config, accountname) + thread = threadutil.InstanceLimitedThread(instancename = 'ACCOUNTLIMIT', + target = account.syncrunner, + name = "Account sync %s" % accountname) + thread.setDaemon(True) + thread.start() + return thread + +def syncitall(accounts, config): + """The target when in multithreading mode for running accounts threads.""" + + threads = threadutil.accountThreads() # The collection of accounts threads. + for accountname in accounts: + # Start a new thread per account and store it in the collection. + threads.add(syncaccount(config, accountname)) + # Wait for the threads to finish. + threads.wait() # Blocks until all accounts are processed. + + class OfflineImap: """The main class that encapsulates the high level use of OfflineImap. @@ -388,9 +410,11 @@ class OfflineImap: self.__sync_singlethreaded(syncaccounts) else: # multithreaded - t = threadutil.ExitNotifyThread(target=syncmaster.syncitall, + t = threadutil.ExitNotifyThread(target=syncitall, name='Sync Runner', kwargs={'accounts': syncaccounts, 'config': self.config}) + # Special exit message for the monitor to stop looping. + t.exit_message = threadutil.STOP_MONITOR t.start() threadutil.monitor() @@ -407,13 +431,12 @@ class OfflineImap: return 1 def __sync_singlethreaded(self, accs): - """Executed if we do not want a separate syncmaster thread + """Executed in singlethreaded mode only. :param accs: A list of accounts that should be synced """ for accountname in accs: - account = offlineimap.accounts.SyncableAccount(self.config, - accountname) + account = accounts.SyncableAccount(self.config, accountname) threading.currentThread().name = "Account sync %s"% accountname account.syncrunner() diff --git a/offlineimap/syncmaster.py b/offlineimap/syncmaster.py deleted file mode 100644 index e469f2c..0000000 --- a/offlineimap/syncmaster.py +++ /dev/null @@ -1,45 +0,0 @@ -# OfflineIMAP synchronization master code -# Copyright (C) 2002-2007 John Goerzen -# -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -from offlineimap.threadutil import accountThreads, InstanceLimitedThread, STOP_MONITOR -from offlineimap.accounts import SyncableAccount -from threading import currentThread - -def syncaccount(config, accountname): - """Return a new running thread for this account.""" - - account = SyncableAccount(config, accountname) - thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT', - target = account.syncrunner, - name = "Account sync %s" % accountname) - thread.setDaemon(True) - thread.start() - return thread - -def syncitall(accounts, config): - """The target when in multithreading mode for running accounts threads.""" - - # Special exit message for the monitor to stop looping so the main thread - # can exit. - currentThread().exit_message = STOP_MONITOR - threads = accountThreads() # The collection of accounts threads. - for accountname in accounts: - # Start a new thread per account and store it in the collection. - threads.add(syncaccount(config, accountname)) - # Wait for the threads to finish. - threads.wait() # Blocks until all accounts are processed. diff --git a/offlineimap/threadutil.py b/offlineimap/threadutil.py index 9e7238b..7ad8ca4 100644 --- a/offlineimap/threadutil.py +++ b/offlineimap/threadutil.py @@ -84,7 +84,7 @@ class accountThreads(object): # Exit-notify threads ###################################################################### -exitthreads = Queue() +exitedThreads = Queue() def monitor(): """An infinite "monitoring" loop watching for finished ExitNotifyThread's. @@ -103,17 +103,20 @@ def monitor(): :type callback: a callable function """ - global exitthreads + global exitedThreads ui = getglobalui() while True: # Loop forever and call 'callback' for each thread that exited try: - # We need a timeout in the get() call, so that ctrl-c can throw - # a SIGINT (http://bugs.python.org/issue1360). A timeout with empty + # We need a timeout in the get() call, so that ctrl-c can throw a + # SIGINT (http://bugs.python.org/issue1360). A timeout with empty # Queue will raise `Empty`. - thread = exitthreads.get(True, 60) - # request to abort when callback returns true + # + # ExitNotifyThread add themselves to the exitedThreads queue once + # they are done (normally or with exception). + thread = exitedThreads.get(True, 60) + # Request to abort when callback returns True. if thread.exit_exception is not None: if isinstance(thread.exit_exception, SystemExit): @@ -128,6 +131,7 @@ def monitor(): " and the ui did not stop the program."% (repr(thread.exit_exception), type(thread.exit_exception))) + # Only the monitor thread has this exit message set. elif thread.exit_message == STOP_MONITOR: break # Exit the loop here. else: @@ -160,9 +164,9 @@ class ExitNotifyThread(Thread): self._exit_stacktrace = None def run(self): - """Allow profiling of a run.""" + """Allow profiling of a run and store exceptions.""" - global exitthreads + global exitedThreads try: if not ExitNotifyThread.profiledir: # normal case Thread.run(self) @@ -183,8 +187,7 @@ class ExitNotifyThread(Thread): tb = traceback.format_exc() self.set_exit_exception(e, tb) - if exitthreads: - exitthreads.put(self, True) + exitedThreads.put(self, True) def set_exit_exception(self, exc, st=None): """Sets Exception and stacktrace of a thread, so that other