[PATCHv2 4b/2] Implement true single-threading

Sebastian Spaeth Sebastian at SSpaeth.de
Mon Jan 10 11:12:43 GMT 2011


Previously, we would spawn child threads for account synchronization
even if we had single-threading enabled. This prevented us from catching
the true location of exceptions, for example. Now, in single-threaded
mode, we perform the account synchronization truely in the main thread
which will ease our debugging.

Signed-off-by: Sebastian Spaeth <Sebastian at SSpaeth.de>
---

 This is version 2, part b of the patch 4 that it replaces. As noted,
 the previous patch 4 led to nonworking code due to a mixup with my
 import statements. I took care to only modify code that actually
 implemented single threading, so this one should be smaller, cleaner
 and more self-contained. Also this version only invokes
 single-threading when we call offlineimap with the "-1" singlethreading
 option. We will do the multithreading as before when there is only a
 single account to sync or syncmaxaccounts is set to 1 (default). We can
 discuss in the future if these cases should also fall under the
 single-threaded code path.

 offlineimap/init.py       |   39 ++++++++++++++++++++++++++++-----------
 offlineimap/syncmaster.py |    4 +---
 2 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/offlineimap/init.py b/offlineimap/init.py
index f652328..9c39e8d 100644
--- a/offlineimap/init.py
+++ b/offlineimap/init.py
@@ -293,11 +293,8 @@ class OfflineImap:
             remoterepos = None
             localrepos = None
     
-            if options.singlethreading:
-                threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
-            else:
-                threadutil.initInstanceLimit("ACCOUNTLIMIT",
-                                             config.getdefaultint("general", "maxsyncaccounts", 1))
+            threadutil.initInstanceLimit("ACCOUNTLIMIT",
+                                         config.getdefaultint("general", "maxsyncaccounts", 1))
     
             for reposname in config.getsectionlist('Repository'):
                 for instancename in ["FOLDER_" + reposname,
@@ -326,15 +323,23 @@ class OfflineImap:
             signal.signal(signal.SIGUSR1,sig_handler)
             signal.signal(signal.SIGUSR2,sig_handler)
     
-            threadutil.initexitnotify()
-            t = threadutil.ExitNotifyThread(target=syncmaster.syncitall,
+            #various initializations that need to be performed:
+            threadutil.initexitnotify()       #TODO: Why?
+            offlineimap.mbnames.init(config, syncaccounts)
+
+            if options.singlethreading:
+                #singlethreaded
+                self.sync_singlethreaded(syncaccounts, config, siglisteners)
+            else:
+                # multithreaded
+                t = threadutil.ExitNotifyThread(target=syncmaster.syncitall,
                                  name='Sync Runner',
                                  kwargs = {'accounts': syncaccounts,
                                            'config': config,
                                            'siglisteners': siglisteners})
-            t.setDaemon(1)
-            t.start()
-            threadutil.exitnotifymonitorloop(threadutil.threadexited)
+                t.setDaemon(1)
+                t.start()
+                threadutil.exitnotifymonitorloop(threadutil.threadexited)
 
         except KeyboardInterrupt:
             ui.terminate(1, errormsg = 'CTRL-C pressed, aborting...')
@@ -344,4 +349,16 @@ class OfflineImap:
         except:
             ui.mainException()
 
-        
+    def sync_singlethreaded(self, accs, config, siglisteners):
+        """Executed if we do not want a separate syncmaster thread
+
+        :param accs: A list of accounts that should be synced
+        :param config: The CustomConfig object
+        :param siglisteners: The signal listeners list, defined in run()
+        """
+        for accountname in accs:
+            account = offlineimap.accounts.SyncableAccount(config, accountname)
+            siglistener = offlineimap.accounts.SigListener()
+            siglisteners.append(siglistener)
+            threading.currentThread().name = "Account sync %s" % accountname
+            account.syncrunner(siglistener=siglistener)
diff --git a/offlineimap/syncmaster.py b/offlineimap/syncmaster.py
index 2da8e3e..5a0a2fb 100644
--- a/offlineimap/syncmaster.py
+++ b/offlineimap/syncmaster.py
@@ -16,7 +16,6 @@
 #    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 import mbnames
 from offlineimap.threadutil import threadlist, InstanceLimitedThread, ExitNotifyThread
 from offlineimap.accounts import SyncableAccount, SigListener
 from threading import currentThread
@@ -33,11 +32,10 @@ def syncaccount(threads, config, accountname, siglisteners):
     thread.setDaemon(1)
     thread.start()
     threads.add(thread)
-    
+
 def syncitall(accounts, config, siglisteners):
     currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
     threads = threadlist()
-    mbnames.init(config, accounts)
     for accountname in accounts:
         syncaccount(threads, config, accountname, siglisteners)
     # Wait for the threads to finish.
-- 
1.7.1





More information about the OfflineIMAP-project mailing list