URI: 
       tWalletSynchronizer had a race condition caused by calling the callback before the Qt event loop (or other initialisation) finished. Ergo we split initialisation and the running of the thread, then use Qt SIGNALs to yield back into the Qt event loop. This ensures that the callback for the servers_list_changed is not called until the main Qt event loop is actually running. - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit b3b910d926c3ab081aa0c5ad2a090909311c631f
   DIR parent e61d478a80e450067f78dfaaef54e0643b26e299
  HTML Author: Amir Taaki <genjix@riseup.net>
       Date:   Wed, 29 Aug 2012 20:49:31 +0100
       
       WalletSynchronizer had a race condition caused by calling the callback before the Qt event loop (or other initialisation) finished. Ergo we split initialisation and the running of the thread, then use Qt SIGNALs to yield back into the Qt event loop. This ensures that the callback for the servers_list_changed is not called until the main Qt event loop is actually running.
       
       Diffstat:
         M electrum                            |       3 ++-
         M lib/gui_lite.py                     |      21 ++++++++++-----------
         M lib/gui_qt.py                       |       3 +++
         M lib/interface.py                    |      18 +++++++++++-------
       
       4 files changed, 26 insertions(+), 19 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -173,7 +173,8 @@ if __name__ == '__main__':
                    sys.exit("Error: Unknown GUI: " + options.gui)
        
                gui = gui.ElectrumGui(wallet)
       -        WalletSynchronizer(wallet,True).start()
       +        interface = WalletSynchronizer(wallet, True, gui.server_list_changed)
       +        interface.start()
        
                try:
                    found = wallet.file_exists
   DIR diff --git a/lib/gui_lite.py b/lib/gui_lite.py
       t@@ -42,14 +42,18 @@ def resize_line_edit_width(line_edit, text_input):
            text_input += "A"
            line_edit.setMinimumWidth(metrics.width(text_input))
        
       -class ElectrumGui:
       +class ElectrumGui(QObject):
        
            def __init__(self, wallet):
       +        super(QObject, self).__init__()
       +
                self.wallet = wallet
                self.app = QApplication(sys.argv)
        
            def main(self, url):
                actuator = MiniActuator(self.wallet)
       +        self.connect(self, SIGNAL("updateservers()"),
       +                     actuator.update_servers_list)
                # Should probably not modify the current path but instead
                # change the behaviour of rsrc(...)
                old_path = QDir.currentPath()
       t@@ -74,6 +78,9 @@ class ElectrumGui:
        
                self.app.exec_()
        
       +    def server_list_changed(self):
       +        self.emit(SIGNAL("updateservers()"))
       +
            def expand(self):
                """Hide the lite mode window and show pro-mode."""
                self.mini.hide()
       t@@ -495,16 +502,13 @@ class ReceivePopup(QDialog):
                QCursor.setPos(center_mouse_pos)
                self.show()
        
       -class MiniActuator(QObject):
       +class MiniActuator:
            """Initialize the definitions relating to themes and 
       -    sending/recieving bitcoins.
       -    """
       +    sending/recieving bitcoins."""
            
            
            def __init__(self, wallet):
                """Retrieve the gui theme used in previous session."""
       -        super(QObject, self).__init__()
       -
                self.wallet = wallet
                self.theme_name = self.wallet.theme
                self.themes = util.load_theme_paths()
       t@@ -549,11 +553,9 @@ class MiniActuator(QObject):
            def set_servers_gui_stuff(self, servers_menu, servers_group):
                self.servers_menu = servers_menu
                self.servers_group = servers_group
       -        self.connect(self, SIGNAL("updateservers()"), self.update_servers_list)
        
            def populate_servers_menu(self):
                interface = self.wallet.interface
       -        interface.servers_loaded_callback = self.server_list_changed
                if not interface.servers:
                    print "No servers loaded yet."
                    self.servers_list = []
       t@@ -583,9 +585,6 @@ class MiniActuator(QObject):
                    server_action.toggled.connect(delegate)
                    self.servers_group.addAction(server_action)
        
       -    def server_list_changed(self):
       -        self.emit(SIGNAL("updateservers()"))
       -
            def update_servers_list(self):
                # Clear servers_group
                for action in self.servers_group.actions():
   DIR diff --git a/lib/gui_qt.py b/lib/gui_qt.py
       t@@ -1430,6 +1430,9 @@ class ElectrumGui:
                if app is None:
                    self.app = QApplication(sys.argv)
        
       +    def server_list_changed(self):
       +        pass
       +
            def waiting_dialog(self):
        
                s = Timer()
   DIR diff --git a/lib/interface.py b/lib/interface.py
       t@@ -306,14 +306,15 @@ class TcpStratumInterface(Interface):
        
        class WalletSynchronizer(threading.Thread):
        
       -    def __init__(self, wallet, loop=False):
       +    def __init__(self, wallet, loop=False, servers_loaded_callback=None):
                threading.Thread.__init__(self)
                self.daemon = True
                self.wallet = wallet
                self.loop = loop
                self.init_interface()
       +        self.servers_loaded_callback = servers_loaded_callback
        
       -    def init_interface(self, servers_loaded_callback=None):
       +    def init_interface(self):
                try:
                    host, port, protocol = self.wallet.server.split(':')
                    port = int(port)
       t@@ -332,10 +333,8 @@ class WalletSynchronizer(threading.Thread):
                    InterfaceClass = TcpStratumInterface
        
                self.interface = InterfaceClass(host, port, self.wallet.debug_server)
       -        self.interface.servers_loaded_callback = servers_loaded_callback
                self.wallet.interface = self.interface
        
       -
            def handle_response(self, r):
                if r is None:
                    return
       t@@ -364,8 +363,13 @@ class WalletSynchronizer(threading.Thread):
                        if ports and version:
                            servers.append((host, ports))
                    self.interface.servers = servers
       -            assert self.interface.servers_loaded_callback is not None
       -            self.interface.servers_loaded_callback()
       +            # TODO: This assert fails with commands so it should be removed
       +            # after we've ascertained it never fails when running the GUI.
       +            assert self.servers_loaded_callback is not None
       +            # servers_loaded_callback is None for commands, but should
       +            # NEVER be None when using the GUI.
       +            if self.servers_loaded_callback is not None:
       +                self.servers_loaded_callback()
        
                elif method == 'blockchain.address.subscribe':
                    addr = params[0]
       t@@ -429,7 +433,7 @@ class WalletSynchronizer(threading.Thread):
                    if self.loop:
                        time.sleep(5)
                        # Server has been changed. Copy callback for new interface.
       -                self.init_interface(self.interface.servers_loaded_callback)
       +                self.init_interface()
                        self.start_interface()
                        continue
                    else: