From a1f40af03367801cf5b92645ee655cab5060c2bd Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Thu, 12 May 2016 05:29:49 +0200 Subject: [PATCH] threading: simplify the monitoring code for threads Signed-off-by: Nicolas Sebrecht --- offlineimap/init.py | 7 +++-- offlineimap/threadutil.py | 56 +++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/offlineimap/init.py b/offlineimap/init.py index 6803fd8..c813fd5 100644 --- a/offlineimap/init.py +++ b/offlineimap/init.py @@ -389,11 +389,10 @@ class OfflineImap: else: # multithreaded t = threadutil.ExitNotifyThread(target=syncmaster.syncitall, - name='Sync Runner', - kwargs = {'accounts': syncaccounts, - 'config': self.config}) + name='Sync Runner', + kwargs={'accounts': syncaccounts, 'config': self.config}) t.start() - threadutil.exitnotifymonitorloop(threadutil.threadexited) + threadutil.exitnotifymonitorloop() if not options.dryrun: offlineimap.mbnames.write(True) diff --git a/offlineimap/threadutil.py b/offlineimap/threadutil.py index 076aafb..1e385b8 100644 --- a/offlineimap/threadutil.py +++ b/offlineimap/threadutil.py @@ -22,7 +22,6 @@ except ImportError: # python3 from queue import Queue, Empty import traceback import os.path -import sys from offlineimap.ui import getglobalui @@ -87,7 +86,7 @@ class threadlist: exitthreads = Queue() -def exitnotifymonitorloop(callback): +def exitnotifymonitorloop(): """An infinite "monitoring" loop watching for finished ExitNotifyThread's. This one is supposed to run in the main thread. @@ -105,39 +104,37 @@ def exitnotifymonitorloop(callback): """ global exitthreads - do_loop = True - while do_loop: + 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 + # 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`. - thrd = exitthreads.get(True, 60) + thread = exitthreads.get(True, 60) # request to abort when callback returns true - do_loop = (callback(thrd) != True) + + if thread.exit_exception is not None: + if isinstance(thread.exit_exception, SystemExit): + # Bring a SystemExit into the main thread. + # Do not send it back to UI layer right now. + # Maybe later send it to ui.terminate? + raise SystemExit + ui.threadException(thread) # Expected to terminate the program. + # Should never hit this line. + raise AssertionError("thread has 'exit_exception' set to" + " '%s' [%s] but this value is unexpected" + " and the ui did not stop the program."% + (repr(thread.exit_exception), type(thread.exit_exception))) + + elif thread.exit_message == NORMAL_EXIT: + break # Exit the loop here. + else: + ui.threadExited(thread) except Empty: pass -def threadexited(thread): - """Called when a thread exits. - - Main thread is aborted when this returns True.""" - - ui = getglobalui() - if thread.exit_exception: - if isinstance(thread.exit_exception, SystemExit): - # Bring a SystemExit into the main thread. - # Do not send it back to UI layer right now. - # Maybe later send it to ui.terminate? - raise SystemExit - ui.threadException(thread) # Expected to terminate - sys.exit(100) # Just in case... - elif thread.exit_message == NORMAL_EXIT: - return True - else: - ui.threadExited(thread) - return False - class ExitNotifyThread(Thread): """This class is designed to alert a "monitor" to the fact that a thread has exited and to provide for the ability for it to find out @@ -145,7 +142,10 @@ class ExitNotifyThread(Thread): bail out when the mainloop dies. The thread can set instance variables self.exit_message for a human - readable reason of the thread exit.""" + readable reason of the thread exit. + + There is one instance of this class at runtime. The main thread waits for + the monitor to end.""" profiledir = None """Class variable that is set to the profile directory if required."""