From d9c828e43c3a2c33ee88fb36ccac3b07a43b1f65 Mon Sep 17 00:00:00 2001 From: Tomas Korbar Date: Wed, 30 Jan 2019 16:55:48 +0100 Subject: [PATCH] Attempt to remove all gdk threads_enter and leave calls Changed used module for handling of threads from _thread to threading. Threading has better API and allows us to distinguish threads more easily. However there is a minor problem. When Gdk Window is passed as a argument to method executed in different thread then it is converted before execution of method into Gtk box. That is why i removed parent argument from connect method of GUI class. Parent argument has been removed from connect method of GUI class. Reasons described above. Connect was always called with the same parent anyway so this does not change its function. Added thread_operations.py file. It contains decorator which simulates some functionalities provided by deprecated functions. Some calls of deprecated functions have been replaced by Gdk_threads_add_idle function. --- Makefile.am | 1 + system-config-printer.py | 113 ++++++++++++++++++--------------------- thread_operations.py | 30 +++++++++++ 3 files changed, 84 insertions(+), 60 deletions(-) create mode 100644 thread_operations.py diff --git a/Makefile.am b/Makefile.am index c2766b78b..3b8fe40cd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,6 +130,7 @@ nobase_pkgdata_DATA= \ smburi.py \ statereason.py \ timedops.py \ + thread_operations.py \ ToolbarSearchEntry.py \ userdefault.py \ ui/AboutDialog.ui \ diff --git a/system-config-printer.py b/system-config-printer.py index f3e266377..5857e0f19 100755 --- a/system-config-printer.py +++ b/system-config-printer.py @@ -23,9 +23,8 @@ # config is generated from config.py.in by configure import config - import sys, os, time, re -import _thread +import threading import dbus import gi try: @@ -94,6 +93,7 @@ def show_help(): import newprinter from newprinter import busy, ready import printerproperties +from thread_operations import thread_safe_blocking_call, SCP_MAIN_THREAD_NAME import ppdippstr ppdippstr.init () @@ -749,6 +749,7 @@ def on_server_settings_activate (self, menuitem): except RuntimeError: self.monitor.update () + @thread_safe_blocking_call def setConnected(self): connected = bool(self.cups) @@ -830,6 +831,7 @@ def getServers(self): known_servers.sort() return known_servers + @thread_safe_blocking_call def populateList(self, prompt_allowed=True): # Save selection of printers. selected_printers = set() @@ -1178,19 +1180,17 @@ def on_connect_activate(self, widget): cups.setUser('') self.connect_user = cups.getUser() # Now start a new thread for connection. - self.connect_thread = _thread.start_new_thread(self.connect, - (self.PrintersWindow,)) + self.connect_thread = threading.Thread(target=self.connect, + name="SCP_CONNECTING_THREAD" + ) + self.connect_thread.start() def update_connecting_pbar (self): ret = True - Gdk.threads_enter () - try: - if not self.ConnectingDialog.get_property ("visible"): - ret = False # stop animation - else: - self.pbarConnecting.pulse () - finally: - Gdk.threads_leave () + if not self.ConnectingDialog.get_property ("visible"): + ret = False # stop animation + else: + self.pbarConnecting.pulse () return ret @@ -1207,7 +1207,7 @@ def on_cancel_connect_clicked(self, widget): self.connect_thread = None self.ConnectingDialog.hide() - def connect(self, parent=None): + def connect(self): """ Open a connection to a new server. Is executed in a separate thread! """ @@ -1232,42 +1232,44 @@ def connect(self, parent=None): nonfatalException () try: - connection = authconn.Connection(parent, + connection = authconn.Connection(self.PrintersWindow, host=self.connect_server, encryption=self.connect_encrypt) except RuntimeError as s: - if self.connect_thread != _thread.get_ident(): return - Gdk.threads_enter() - try: - self.ConnectingDialog.hide() - self.cups = None - self.setConnected() - self.populateList() - show_IPP_Error(None, s, parent) - finally: - Gdk.threads_leave() + if self.connect_thread != threading.currentThread(): return + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + self.ConnectingDialog.hide, + ) + self.cups = None + self.setConnected() + self.populateList() + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + show_IPP_Error, + None, s, self.PrintersWindow + ) return except cups.IPPError as e: (e, s) = e.args - if self.connect_thread != _thread.get_ident(): return - Gdk.threads_enter() - try: - self.ConnectingDialog.hide() - self.cups = None - self.setConnected() - self.populateList() - show_IPP_Error(e, s, parent) - finally: - Gdk.threads_leave() + if self.connect_thread != threading.currentThread(): return + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + self.ConnectingDialog.hide, + ) + self.cups = None + self.setConnected() + self.populateList() + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + show_IPP_Error, + None, s, self.PrintersWindow + ) return except: nonfatalException () - if self.connect_thread != _thread.get_ident(): return - Gdk.threads_enter() - + if self.connect_thread != threading.currentThread(): return try: - self.ConnectingDialog.hide() + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + self.ConnectingDialog.hide, + ) self.cups = connection self.setConnected() self.populateList() @@ -1276,12 +1278,13 @@ def connect(self, parent=None): self.cups = None self.setConnected() self.populateList() - show_HTTP_Error(s, parent) + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + show_HTTP_Error, + s, self.PrintersWindow + ) except: nonfatalException () - Gdk.threads_leave() - def reconnect (self): """Reconnect to CUPS after the server has reloaded.""" # libcups would handle the reconnection if we just told it to @@ -2058,23 +2061,19 @@ def on_start_service_reply (self, *args): GLib.timeout_add_seconds (1, self.service_started_try) def service_started_try (self): - Gdk.threads_enter () - try: - self.on_btnRefresh_clicked (None) - finally: - Gdk.threads_leave () + Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, + self.on_btnRefresh_clicked, + None + ) + GLib.timeout_add_seconds (1, self.service_started_retry) return False def service_started_retry (self): if not self.cups: - Gdk.threads_enter () - try: self.on_btnRefresh_clicked (None) self.btnStartService.set_sensitive (True) - finally: - Gdk.threads_leave () return False @@ -2180,11 +2179,7 @@ def on_printer_modified (self, obj, name, ppd_has_changed): def defer_refresh (self): def deferred_refresh (): self.populateList_timer = None - Gdk.threads_enter () - try: - self.populateList (prompt_allowed=False) - finally: - Gdk.threads_leave () + self.populateList (prompt_allowed=False) return False if self.populateList_timer: @@ -2222,6 +2217,8 @@ def cups_connection_recovered (self, mon): def main(show_jobs): cups.setUser (os.environ.get ("CUPS_USER", cups.getUser())) + # set name for main thread + threading.currentThread().setName(SCP_MAIN_THREAD_NAME) Gdk.threads_init () from dbus.glib import DBusGMainLoop DBusGMainLoop (set_as_default=True) @@ -2233,11 +2230,7 @@ def main(show_jobs): else: mainwindow = GUI() - Gdk.threads_enter () - try: - Gtk.main() - finally: - Gdk.threads_leave () + Gtk.main() if __name__ == "__main__": import getopt diff --git a/thread_operations.py b/thread_operations.py new file mode 100644 index 000000000..c05130f00 --- /dev/null +++ b/thread_operations.py @@ -0,0 +1,30 @@ +import threading +gi.require_version('Gdk', '3.0') +from gi.repository import Gdk, GLib + +SCP_MAIN_THREAD_NAME = "SCP_MAIN_THREAD" + +def thread_safe_blocking_call(function): + """ Make function/method thread safe and block until its call is finished + """ + + blocker = threading.Event() + + def inner_wrapper(*args, **kwargs): + function(*args, **kwargs) + blocker.set() + return False + + def wrapper(*args, **kwargs): + # if this is the main thread then simply return function + if threading.current_thread().name == SCP_MAIN_THREAD_NAME: + return function(*args, **kwargs) + Gdk.threads_add_idle( + GLib.PRIORITY_DEFAULT_IDLE, + inner_wrapper, + *args, + **kwargs + ) + blocker.wait() + + return wrapper