Sat, 18 Apr 2015 19:14:15 +0200
Implemented the Cancel logic for batch checks.
--- a/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py Fri Apr 17 18:57:38 2015 +0200 +++ b/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py Sat Apr 18 19:14:15 2015 +0200 @@ -112,25 +112,30 @@ return __checkCodeStyle(filename, source, args) -def codeStyleBatchCheck(argumentsList, send, fx): +def codeStyleBatchCheck(argumentsList, send, fx, cancelled): """ Module function to check code style for a batch of files. @param argumentsList list of arguments tuples as given for codeStyleCheck - @param send reference to send method + @param send reference to send function (function) @param fx registered service name (string) + @param cancelled reference to function checking for a cancellation + (function) """ try: NumberOfProcesses = multiprocessing.cpu_count() + if NumberOfProcesses >= 1: + NumberOfProcesses -= 1 except NotImplementedError: - NumberOfProcesses = 4 + NumberOfProcesses = 1 # Create queues taskQueue = multiprocessing.Queue() doneQueue = multiprocessing.Queue() - # Submit tasks - for task in argumentsList: + # Submit tasks (initially two time number of processes + initialTasks = 2 * NumberOfProcesses + for task in argumentsList[:initialTasks]: taskQueue.put(task) # Start worker processes @@ -139,9 +144,15 @@ .start() # Get and send results + endIndex = len(argumentsList) - initialTasks for i in range(len(argumentsList)): filename, result = doneQueue.get() send(fx, filename, result) + if cancelled(): + # just exit the loop ignoring the results of queued tasks + break + if i < endIndex: + taskQueue.put(argumentsList[i + initialTasks]) # Tell child processes to stop for i in range(NumberOfProcesses):
--- a/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py Fri Apr 17 18:57:38 2015 +0200 +++ b/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py Sat Apr 18 19:14:15 2015 +0200 @@ -28,7 +28,6 @@ from . import pep8 -# TODO: implement CANCEL for batch jobs (if possible) class CodeStyleCheckerDialog(QDialog, Ui_CodeStyleCheckerDialog): """ Class implementing a dialog to show the results of the code style check. @@ -445,9 +444,6 @@ """ self.__lastFileItem = None - # Cancel doesn't work for batch jobs - self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) - self.checkProgressLabel.setPath(self.tr("Preparing files...")) progress = 0 @@ -587,10 +583,8 @@ if self.noResults: QTreeWidgetItem(self.resultList, [self.tr('No issues found.')]) QApplication.processEvents() - self.statisticsButton.setEnabled(False) self.showButton.setEnabled(False) else: - self.statisticsButton.setEnabled(True) self.showButton.setEnabled(True) self.resultList.header().resizeSections(QHeaderView.ResizeToContents) self.resultList.header().setStretchLastSection(True) @@ -856,7 +850,10 @@ if button == self.buttonBox.button(QDialogButtonBox.Close): self.close() elif button == self.buttonBox.button(QDialogButtonBox.Cancel): - self.__finish() + if self.__batch: + self.styleCheckService.cancelStyleBatchCheck() + else: + self.__finish() elif button == self.showButton: self.on_showButton_clicked() elif button == self.statisticsButton:
--- a/Plugins/PluginCodeStyleChecker.py Fri Apr 17 18:57:38 2015 +0200 +++ b/Plugins/PluginCodeStyleChecker.py Sat Apr 18 19:14:15 2015 +0200 @@ -203,6 +203,13 @@ data[lang]) self.batchesFinished = False + def cancelStyleBatchCheck(self): + """ + Public method to cancel all batch jobs. + """ + for lang in ['Python2', 'Python3']: + self.backgroundService.requestCancel(lang) + def __translateStyleCheck(self, fn, codeStyleCheckerStats, results): """ Private slot called after perfoming a style check on one file.
--- a/Utilities/BackgroundClient.py Fri Apr 17 18:57:38 2015 +0200 +++ b/Utilities/BackgroundClient.py Sat Apr 18 19:14:15 2015 +0200 @@ -86,7 +86,7 @@ Private methode to receive the given length of bytes. @param length bytes to receive (int) - @return received bytes or None if connection closed (str) + @return received bytes or None if connection closed (bytes) """ data = b'' while len(data) < length: @@ -96,20 +96,54 @@ data += newData return data + def __peek(self, length): + """ + Private methode to peek the given length of bytes. + + @param length bytes to receive (int) + @return received bytes (bytes) + """ + data = b'' + self.connection.setblocking(False) + try: + data = self.connection.recv(length, socket.MSG_PEEK) + except socket.error: + pass + self.connection.setblocking(True) + return data + + def __cancelled(self): + """ + Private method to check for a job cancellation. + + @return flag indicating a cancellation (boolean) + """ + msg = self.__peek(struct.calcsize(b'!II') + 6) + if msg[-6:] == b"CANCEL": + # get rid of the message data + self.__peek(struct.calcsize(b'!II') + 6) + return True + else: + return False + def run(self): """ Public method implementing the main loop of the client. """ try: while True: - header = self.__receive(8) + header = self.__receive(struct.calcsize(b'!II')) # Leave main loop if connection was closed. if not header: break length, datahash = struct.unpack(b'!II', header) + messageType = self.__receive(6) packedData = self.__receive(length) + if messageType != b"JOB ": + continue + assert adler32(packedData) & 0xffffffff == datahash, \ 'Hashes not equal' if sys.version_info[0] == 3: @@ -121,7 +155,7 @@ elif fx.startswith("batch_"): callback = self.batchServices.get(fx) if callback: - callback(data, self.__send, fx) + callback(data, self.__send, fx, self.__cancelled) ret = "__DONE__" else: ret = 'Unknown batch service.'
--- a/Utilities/BackgroundService.py Fri Apr 17 18:57:38 2015 +0200 +++ b/Utilities/BackgroundService.py Sat Apr 18 19:14:15 2015 +0200 @@ -132,6 +132,7 @@ header = struct.pack( b'!II', len(packedData), adler32(packedData) & 0xffffffff) connection.write(header) + connection.write(b'JOB ') # 6 character message type connection.write(packedData) def __receive(self, lang): @@ -142,7 +143,7 @@ """ connection = self.connections[lang] while connection.bytesAvailable(): - header = connection.read(8) + header = connection.read(struct.calcsize(b'!II')) length, datahash = struct.unpack(b'!II', header) packedData = b'' @@ -290,6 +291,20 @@ self.__queue.append(args) self.__processQueue() + def requestCancel(self, lang): + """ + Public method to ask a batch job to terminate. + + @param lang language to connect to (str) + """ + connection = self.connections.get(lang) + if connection is None: + return + else: + header = struct.pack(b'!II', 0, 0) + connection.write(header) + connection.write(b'CANCEL') # 6 character message type + def serviceConnect( self, fx, lang, modulepath, module, callback, onErrorCallback=None, onBatchDone=None):