Cancel Senging Message Feature

This commit is contained in:
Andor 2013-12-08 23:12:03 +04:00
parent e423343e9a
commit 554b4f8cce
3 changed files with 70 additions and 8 deletions

View File

@ -390,6 +390,12 @@ class MyForm(QtGui.QMainWindow):
_translate( _translate(
"MainWindow", "Copy destination address to clipboard"), "MainWindow", "Copy destination address to clipboard"),
self.on_action_SentClipboard) self.on_action_SentClipboard)
self.actionCancelPoW = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Cancel sending"), self.on_action_CancelPoW)
self.actionContinuePoW = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Continue sending"), self.on_action_ContinuePoW)
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction( self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Force send"), self.on_action_ForceSend) "MainWindow", "Force send"), self.on_action_ForceSend)
@ -849,6 +855,9 @@ class MyForm(QtGui.QMainWindow):
elif status == 'toodifficult': elif status == 'toodifficult':
statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg( statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg(
unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8'))
elif status == 'PoW_Cancelled':
statusText = _translate("MainWindow", "Problem: The sending was cancelled. %1").arg(
unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8'))
elif status == 'badkey': elif status == 'badkey':
statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg( statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg(
unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8')) unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(lastactiontime)),'utf-8'))
@ -2718,6 +2727,31 @@ class MyForm(QtGui.QMainWindow):
clipboard = QtGui.QApplication.clipboard() clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(addressAtCurrentRow)) clipboard.setText(str(addressAtCurrentRow))
def on_action_CancelPoW(self):
#The user wants to cancel the sending
if shared.PoWQueue.empty() == True: #The PoW calculation finished
QMessageBox.about(self, _translate("MainWindow", "PoW Finished"), _translate(
"MainWindow", "The message has already been sent!"))
else: #The PoW calculation is still in progress
if shared.PoWQueue.get() == 'PoW_Single_Thread': #It is the single threaded version
QMessageBox.about(self, _translate("MainWindow", "PoW Cancelled"), _translate(
"MainWindow", "The sending was cancelled!"))
else: #The multi threaded version is different, because of the possible overhead of the termination of the threads.
QMessageBox.about(self, _translate("MainWindow", "PoW Cancellation"), _translate(
"MainWindow", "The cancellation of sending can take up to a few seconds because of the multi-threaded environment. If the message is not be sent during this period it will be cancelled!"))
time.sleep(0.2) # We need to give some time to the PoW process to terminate and change the status of the message
self.loadSent()
def on_action_ContinuePoW(self):
#Continue sending of a message if it was cancelled by the user
currentRow = self.ui.tableWidgetSent.selectedIndexes()[0].row()
ackdataToContinue = str(self.ui.tableWidgetSent.item(
currentRow, 3).data(Qt.UserRole).toPyObject())
sqlExecute('''UPDATE sent SET status='msgqueued' WHERE status='PoW_Cancelled' AND ackdata=?''', ackdataToContinue)
self.statusBar().showMessage(_translate(
"MainWindow", "The sending will be continued."))
shared.workerQueue.put(('sendmessage', ''))
# Group of functions for the Address Book dialog box # Group of functions for the Address Book dialog box
def on_action_AddressBookNew(self): def on_action_AddressBookNew(self):
self.click_pushButtonAddAddressBook() self.click_pushButtonAddAddressBook()
@ -3060,6 +3094,12 @@ class MyForm(QtGui.QMainWindow):
status, = row status, = row
if status == 'toodifficult': if status == 'toodifficult':
self.popMenuSent.addAction(self.actionForceSend) self.popMenuSent.addAction(self.actionForceSend)
if status == 'PoW_Cancelled':
self.popMenuSent.addSeparator()
self.popMenuSent.addAction(self.actionContinuePoW)
if (status == 'doingmsgpow') and (shared.PoWQueue.empty() == False): #The PoW of a message is calculating
self.popMenuSent.addSeparator()
self.popMenuSent.addAction(self.actionCancelPoW)
self.popMenuSent.exec_(self.ui.tableWidgetSent.mapToGlobal(point)) self.popMenuSent.exec_(self.ui.tableWidgetSent.mapToGlobal(point))
def inboxSearchLineEditPressed(self): def inboxSearchLineEditPressed(self):

View File

@ -1,4 +1,4 @@
#import shared import shared
#import time #import time
#from multiprocessing import Pool, cpu_count #from multiprocessing import Pool, cpu_count
import hashlib import hashlib
@ -30,16 +30,19 @@ def _pool_worker(nonce, initialHash, target, pool_size):
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
return [trialValue, nonce] return [trialValue, nonce]
def _doSafePoW(target, initialHash): def _doSafePoW(target, initialHash, cancellable):
nonce = 0 nonce = 0
trialValue = float('inf') trialValue = float('inf')
while trialValue > target: while trialValue > target:
if (shared.PoWQueue.empty() == True) and cancellable: #If the PoW is cancellable it can be interrupted
return [-1,-1] #Special value for differentiation
nonce += 1 nonce += 1
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
if cancellable: shared.PoWQueue.get() #If the PoW is cancellable we need to communicate its end to the UI
return [trialValue, nonce] return [trialValue, nonce]
def _doFastPoW(target, initialHash): def _doFastPoW(target, initialHash, cancellable):
import shared
import time import time
from multiprocessing import Pool, cpu_count from multiprocessing import Pool, cpu_count
try: try:
@ -62,16 +65,33 @@ def _doFastPoW(target, initialHash):
while True: while True:
time.sleep(10) # Don't let this thread return here; it will return nothing and cause an exception in bitmessagemain.py time.sleep(10) # Don't let this thread return here; it will return nothing and cause an exception in bitmessagemain.py
return return
if (shared.PoWQueue.empty() == True) and cancellable: #If the PoW is cancellable it can be interrupted
pool.terminate()
pool.join() #Wait for the workers to exit...
return [-1, -1] #Special value for differentiation
for i in range(pool_size): for i in range(pool_size):
if result[i].ready(): if result[i].ready():
result = result[i].get() result = result[i].get()
pool.terminate() pool.terminate()
pool.join() #Wait for the workers to exit... pool.join() #Wait for the workers to exit...
if cancellable: shared.PoWQueue.get() #If the PoW is cancellable we need to communicate its end to the UI
return result[0], result[1] return result[0], result[1]
time.sleep(0.2) time.sleep(0.2)
def run(target, initialHash): def run(target, initialHash, cancellable):
import time
#Only message PoW calculations are cancellable, Key requests are not.
if cancellable:
#If the PoW is cancellable we need to communicate its beginning to the UI
if frozen == "macosx_app" or not frozen: if frozen == "macosx_app" or not frozen:
return _doFastPoW(target, initialHash) shared.PoWQueue.put('PoW_Single_Thread')
else: else:
return _doSafePoW(target, initialHash) shared.PoWQueue.put('PoW_Multi_Thread')
while shared.PoWQueue.empty() == True: #Necessary to wait because of the interprocess/interthread communication
time.sleep(0.1)
if frozen == "macosx_app" or not frozen:
return _doFastPoW(target, initialHash, cancellable)
else:
return _doSafePoW(target, initialHash, cancellable)

View File

@ -14,6 +14,7 @@ import ConfigParser
import os import os
import pickle import pickle
import Queue import Queue
from multiprocessing import Queue as MQueue #A Multiproccessing Queue is necessary for the PoW cancellation.
import random import random
import socket import socket
import sys import sys
@ -39,6 +40,7 @@ broadcastSendersForWhichImWatching = {}
workerQueue = Queue.Queue() workerQueue = Queue.Queue()
UISignalQueue = Queue.Queue() UISignalQueue = Queue.Queue()
addressGeneratorQueue = Queue.Queue() addressGeneratorQueue = Queue.Queue()
PoWQueue = MQueue() #Multithreaded queue for interprocess communication. It is used for the PoW calculation
knownNodesLock = threading.Lock() knownNodesLock = threading.Lock()
knownNodes = {} knownNodes = {}
sendDataQueues = [] #each sendData thread puts its queue in this list. sendDataQueues = [] #each sendData thread puts its queue in this list.