From 3dbb4d5ce9f6ab901d624760cfa0e396b90ea049 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 20 Apr 2016 15:33:01 +0200 Subject: [PATCH] GUI shutdown improvements - it shows that it needs to wait for PoW to finish - it waits a bit for new objects to be distributed - it displays a better progress indicator in the status bar Previously, people who don't understand how PyBitmessage works sometimes shut it down immediately after they wrote a message. This would have caused the message to be stuck in the queue locally and not sent. Now, it will indicate that the PoW still needs to work, and it will wait a bit longer so that the message can spread. It's not a completely correct approach, because it does not know whether the message was really retrieved after the "inv" notification was sent. --- src/bitmessageqt/__init__.py | 72 ++++++++++++++++++++++++++++++++--- src/class_objectHashHolder.py | 3 ++ src/class_singleWorker.py | 3 ++ src/shared.py | 2 + 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a36629be..449308c3 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -67,6 +67,8 @@ from utils import * from collections import OrderedDict from account import * from dialogs import AddAddressDialog +from class_objectHashHolder import objectHashHolder +from class_singleWorker import singleWorker def _translate(context, text): return QtGui.QApplication.translate(context, text) @@ -2644,7 +2646,64 @@ class MyForm(settingsmixin.SMainWindow): if reply is QtGui.QMessageBox.No: return ''' + + self.statusBar().showMessage(_translate( + "MainWindow", "Shutting down PyBitmessage... %1%%").arg(str(0))) + + # check if PoW queue empty + maxWorkerQueue = 0 + curWorkerQueue = 1 + while curWorkerQueue > 0: + # worker queue size + curWorkerQueue = shared.workerQueue.qsize() + # if worker is busy add 1 + for thread in threading.enumerate(): + try: + if isinstance(thread, singleWorker): + curWorkerQueue += thread.busy + except: + pass + if curWorkerQueue > maxWorkerQueue: + maxWorkerQueue = curWorkerQueue + if curWorkerQueue > 0: + self.statusBar().showMessage(_translate("MainWindow", "Waiting for PoW to finish... %1%").arg(str(50 * (maxWorkerQueue - curWorkerQueue) / maxWorkerQueue))) + time.sleep(0.5) + QtCore.QCoreApplication.processEvents() + + self.statusBar().showMessage(_translate("MainWindow", "Shutting down Pybitmessage... %1%").arg(str(50))) + + QtCore.QCoreApplication.processEvents() + if maxWorkerQueue > 0: + time.sleep(0.5) # a bit of time so that the hashHolder is populated + QtCore.QCoreApplication.processEvents() + + # check if objectHashHolder empty + self.statusBar().showMessage(_translate("MainWindow", "Waiting for objects to be sent... %1%").arg(str(50))) + maxWaitingObjects = 0 + curWaitingObjects = 1 + while curWaitingObjects > 0: + curWaitingObjects = 0 + for thread in threading.enumerate(): + try: + if isinstance(thread, objectHashHolder): + curWaitingObjects += thread.hashCount() + except: + pass + if curWaitingObjects > maxWaitingObjects: + maxWaitingObjects = curWaitingObjects + if curWaitingObjects > 0: + self.statusBar().showMessage(_translate("MainWindow", "Waiting for objects to be sent... %1%").arg(str(50 + 20 * (maxWaitingObjects - curWaitingObjects) / maxWaitingObjects))) + time.sleep(0.5) + QtCore.QCoreApplication.processEvents() + + QtCore.QCoreApplication.processEvents() + if maxWorkerQueue > 0 or maxWaitingObjects > 0: + time.sleep(10) # a bit of time so that the other nodes retrieve the objects + QtCore.QCoreApplication.processEvents() + # save state and geometry self and all widgets + self.statusBar().showMessage(_translate("MainWindow", "Saving settings... %1%").arg(str(70))) + QtCore.QCoreApplication.processEvents() self.saveSettings() for attr, obj in self.ui.__dict__.iteritems(): if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): @@ -2652,19 +2711,20 @@ class MyForm(settingsmixin.SMainWindow): if callable (saveMethod): obj.saveSettings() + self.statusBar().showMessage(_translate("MainWindow", "Shutting down core... %1%").arg(str(80))) + QtCore.QCoreApplication.processEvents() shared.doCleanShutdown() + self.statusBar().showMessage(_translate("MainWindow", "Stopping notifications... %1%").arg(str(90))) + QtCore.QCoreApplication.processEvents() self.tray.hide() # unregister the messaging system if self.mmapp is not None: self.mmapp.unregister() -# settings = QSettings("Bitmessage", "PyBitmessage") -# settings.setValue("geometry", self.saveGeometry()) -# settings.setValue("state", self.saveState()) - - self.statusBar().showMessage(_translate( - "MainWindow", "All done. Closing user interface...")) + self.statusBar().showMessage(_translate("MainWindow", "Shutdown imminent... %1%").arg(str(100))) + QtCore.QCoreApplication.processEvents() shared.thisapp.cleanup() + logger.info("Shutdown complete") os._exit(0) # window close event diff --git a/src/class_objectHashHolder.py b/src/class_objectHashHolder.py index 327656db..e4e2c6d8 100644 --- a/src/class_objectHashHolder.py +++ b/src/class_objectHashHolder.py @@ -47,6 +47,9 @@ class objectHashHolder(threading.Thread): def holdPeer(self,peerDetails): self.collectionOfPeerLists[random.randrange(0, self.size)].append(peerDetails) + + def hashCount(self): + return sum([len(x) for x in self.collectionOfHashLists]) def close(self): self.shutdown = True diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 322b2390..6adadd44 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -87,7 +87,9 @@ class singleWorker(threading.Thread, StoppableThread): self.sendBroadcast() while shared.shutdown == 0: + self.busy = 0 command, data = shared.workerQueue.get() + self.busy = 1 if command == 'sendmessage': try: self.sendMsg() @@ -114,6 +116,7 @@ class singleWorker(threading.Thread, StoppableThread): except: pass elif command == 'stopThread': + self.busy = 0 return else: logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command) diff --git a/src/shared.py b/src/shared.py index 315e5671..6eda2e11 100644 --- a/src/shared.py +++ b/src/shared.py @@ -522,6 +522,8 @@ def doCleanShutdown(): logger.info('Clean shutdown complete.') thisapp.cleanup() os._exit(0) + else: + logger.info('Core shutdown complete.') # If you want to command all of the sendDataThreads to do something, like shutdown or send some data, this # function puts your data into the queues for each of the sendDataThreads. The sendDataThreads are