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.
This commit is contained in:
mailchuck 2016-04-20 15:33:01 +02:00 committed by Peter Surda
parent 167da731d0
commit 3dbb4d5ce9
4 changed files with 74 additions and 6 deletions

View File

@ -67,6 +67,8 @@ from utils import *
from collections import OrderedDict from collections import OrderedDict
from account import * from account import *
from dialogs import AddAddressDialog from dialogs import AddAddressDialog
from class_objectHashHolder import objectHashHolder
from class_singleWorker import singleWorker
def _translate(context, text): def _translate(context, text):
return QtGui.QApplication.translate(context, text) return QtGui.QApplication.translate(context, text)
@ -2644,7 +2646,64 @@ class MyForm(settingsmixin.SMainWindow):
if reply is QtGui.QMessageBox.No: if reply is QtGui.QMessageBox.No:
return 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 # save state and geometry self and all widgets
self.statusBar().showMessage(_translate("MainWindow", "Saving settings... %1%").arg(str(70)))
QtCore.QCoreApplication.processEvents()
self.saveSettings() self.saveSettings()
for attr, obj in self.ui.__dict__.iteritems(): for attr, obj in self.ui.__dict__.iteritems():
if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin):
@ -2652,19 +2711,20 @@ class MyForm(settingsmixin.SMainWindow):
if callable (saveMethod): if callable (saveMethod):
obj.saveSettings() obj.saveSettings()
self.statusBar().showMessage(_translate("MainWindow", "Shutting down core... %1%").arg(str(80)))
QtCore.QCoreApplication.processEvents()
shared.doCleanShutdown() shared.doCleanShutdown()
self.statusBar().showMessage(_translate("MainWindow", "Stopping notifications... %1%").arg(str(90)))
QtCore.QCoreApplication.processEvents()
self.tray.hide() self.tray.hide()
# unregister the messaging system # unregister the messaging system
if self.mmapp is not None: if self.mmapp is not None:
self.mmapp.unregister() self.mmapp.unregister()
# settings = QSettings("Bitmessage", "PyBitmessage") self.statusBar().showMessage(_translate("MainWindow", "Shutdown imminent... %1%").arg(str(100)))
# settings.setValue("geometry", self.saveGeometry()) QtCore.QCoreApplication.processEvents()
# settings.setValue("state", self.saveState())
self.statusBar().showMessage(_translate(
"MainWindow", "All done. Closing user interface..."))
shared.thisapp.cleanup() shared.thisapp.cleanup()
logger.info("Shutdown complete")
os._exit(0) os._exit(0)
# window close event # window close event

View File

@ -47,6 +47,9 @@ class objectHashHolder(threading.Thread):
def holdPeer(self,peerDetails): def holdPeer(self,peerDetails):
self.collectionOfPeerLists[random.randrange(0, self.size)].append(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): def close(self):
self.shutdown = True self.shutdown = True

View File

@ -87,7 +87,9 @@ class singleWorker(threading.Thread, StoppableThread):
self.sendBroadcast() self.sendBroadcast()
while shared.shutdown == 0: while shared.shutdown == 0:
self.busy = 0
command, data = shared.workerQueue.get() command, data = shared.workerQueue.get()
self.busy = 1
if command == 'sendmessage': if command == 'sendmessage':
try: try:
self.sendMsg() self.sendMsg()
@ -114,6 +116,7 @@ class singleWorker(threading.Thread, StoppableThread):
except: except:
pass pass
elif command == 'stopThread': elif command == 'stopThread':
self.busy = 0
return return
else: else:
logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command) logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command)

View File

@ -522,6 +522,8 @@ def doCleanShutdown():
logger.info('Clean shutdown complete.') logger.info('Clean shutdown complete.')
thisapp.cleanup() thisapp.cleanup()
os._exit(0) 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 # 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 # function puts your data into the queues for each of the sendDataThreads. The sendDataThreads are