Skip to content

Commit 47e2df8

Browse files
committed
Freezing message parser fix #2
- this has been tested on Windows as well, and has been cleaned up. There is now a permanent parser thread, and it restarts when the parsing takes more than 1 second - Fixes #900
1 parent 8f19429 commit 47e2df8

4 files changed

Lines changed: 49 additions & 27 deletions

File tree

‎src/bitmessageqt/messageview.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from PyQt4 import QtCore, QtGui
22

3+
import multiprocessing
4+
import Queue
35
from urlparse import urlparse
46
from safehtmlparser import *
57

‎src/bitmessageqt/safehtmlparser.py‎

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
from HTMLParser import HTMLParser
22
import inspect
3+
import multiprocessing
34
import re
5+
import Queue
46
from urllib import quote, quote_plus
57
from urlparse import urlparse
6-
import multiprocessing
7-
import Queue
88
from debug import logger
9+
from shared import parserInputQueue, parserOutputQueue, parserProcess, parserLock
910

10-
def regexpSubprocess(queue):
11-
try:
12-
result = queue.get()
13-
result = SafeHTMLParser.uriregex1.sub(
14-
r'<a href="\1">\1</a>',
15-
result)
16-
result = SafeHTMLParser.uriregex2.sub(r'<a href="\1&', result)
17-
result = SafeHTMLParser.emailregex.sub(r'<a href="mailto:\1">\1</a>', result)
18-
except:
19-
pass
20-
else:
21-
queue.put(result)
11+
def regexpSubprocess(parserInputQueue, parserOutputQueue):
12+
for data in iter(parserInputQueue.get, None):
13+
if data is None:
14+
break;
15+
try:
16+
result = SafeHTMLParser.uriregex1.sub(
17+
r'<a href="\1">\1</a>',
18+
data)
19+
result = SafeHTMLParser.uriregex2.sub(r'<a href="\1&', result)
20+
result = SafeHTMLParser.emailregex.sub(r'<a href="mailto:\1">\1</a>', result)
21+
parserOutputQueue.put(result)
22+
except SystemExit:
23+
break;
24+
except:
25+
break;
2226

2327
class SafeHTMLParser(HTMLParser):
2428
# from html5lib.sanitiser
@@ -106,22 +110,28 @@ def handle_entityref(self, name):
106110
self.sanitised += "&" + name + ";"
107111

108112
def feed(self, data):
113+
global parserLock, parserProcess, parserInputQueue, parserOutputQueue
109114
HTMLParser.feed(self, data)
110115
tmp = SafeHTMLParser.multi_replace(data)
111116
tmp = unicode(tmp, 'utf-8', 'replace')
112-
113-
queue = multiprocessing.Queue()
114-
parser = multiprocessing.Process(target=regexpSubprocess, name="RegExParser", args=(queue,))
115-
parser.start()
116-
queue.put(tmp)
117-
parser.join(1)
118-
if parser.is_alive():
119-
parser.terminate()
120-
parser.join()
117+
118+
parserLock.acquire()
119+
if parserProcess is None:
120+
parserProcess = multiprocessing.Process(target=regexpSubprocess, name="RegExParser", args=(parserInputQueue, parserOutputQueue))
121+
parserProcess.start()
122+
parserLock.release()
123+
parserInputQueue.put(tmp)
121124
try:
122-
tmp = queue.get(False)
123-
except (Queue.Empty, StopIteration) as e:
125+
tmp = parserOutputQueue.get(True, 1)
126+
except Queue.Empty:
124127
logger.error("Regular expression parsing timed out, not displaying links")
128+
parserLock.acquire()
129+
parserProcess.terminate()
130+
parserProcess = multiprocessing.Process(target=regexpSubprocess, name="RegExParser", args=(parserInputQueue, parserOutputQueue))
131+
parserProcess.start()
132+
parserLock.release()
133+
else:
134+
pass
125135
self.raw += tmp
126136

127137
def is_html(self, text = None, allow_picture = False):

‎src/helper_generic.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ def convertStringToInt(s):
4444
def signal_handler(signal, frame):
4545
logger.error("Got signal %i in %s/%s", signal, current_process().name, current_thread().name)
4646
if current_process().name == "RegExParser":
47-
sys.exit(0)
47+
# on Windows this isn't triggered, but it's fine, it has its own process termination thing
48+
print "RegExParser interrupted"
49+
raise SystemExit
4850
if current_process().name != "MainProcess":
4951
raise StopIteration("Interrupted")
5052
if current_thread().name != "MainThread":

‎src/shared.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import pickle
1717
import Queue
1818
import random
19-
from multiprocessing import active_children
19+
from multiprocessing import active_children, Queue as mpQueue, Lock as mpLock
2020
from signal import SIGTERM
2121
import socket
2222
import sys
@@ -48,6 +48,10 @@
4848
broadcastSendersForWhichImWatching = {}
4949
workerQueue = Queue.Queue()
5050
UISignalQueue = Queue.Queue()
51+
parserInputQueue = mpQueue()
52+
parserOutputQueue = mpQueue()
53+
parserProcess = None
54+
parserLock = mpLock()
5155
addressGeneratorQueue = Queue.Queue()
5256
knownNodesLock = threading.Lock()
5357
knownNodes = {}
@@ -504,6 +508,10 @@ def isProofOfWorkSufficient(data,
504508
def doCleanShutdown():
505509
global shutdown, thisapp
506510
shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit.
511+
try:
512+
parserInputQueue.put(None, False)
513+
except Queue.Full:
514+
pass
507515
for child in active_children():
508516
try:
509517
logger.info("Killing PoW child %i", child.pid)

0 commit comments

Comments
 (0)