Steps to reproduce:
Case 1
- Configure NVDA to use a braille display, or open NVDA's Braille Viewer from the Tools menu.
- Open a new document in Microsoft Word.
- Keep typing random text rather fast with lots of spaces such that the words are all spelling errors.
Actual behavior:
NVDA eventually freezes.
Expected behavior:
NVDA should not freeze.
technical info
It seems that the Python garbage collector is allowed to run within Queue.put.
This then means that comtypes might call Release on a COM object in a del method, which in tern may cause a dead lock between NvDA and NVDAhelper in-process code in an app like Microsoft Word, when handling typed characters.
See this Python stack:
Python stack for thread 4108:
File "C:\Users\mick\programming\git\nvda\source\NVDAHelper.py", line 401, in nvdaControllerInternal_typedCharacterNotify
eventHandler.queueEvent("typedCharacter",focus,ch=ch)
File "C:\Users\mick\programming\git\nvda\source\eventHandler.py", line 44, in queueEvent
queueHandler.queueFunction(queueHandler.eventQueue,_queueEventCallback,eventName,obj,kwargs)
File "C:\Users\mick\programming\git\nvda\source\queueHandler.py", line 37, in queueFunction
queue.put_nowait((func,args,kwargs))
File "C:\Program Files (x86)\Python37-32\lib\queue.py", line 190, in put_nowait
return self.put(item, block=False)
File "C:\Program Files (x86)\Python37-32\lib\queue.py", line 151, in put
self.not_empty.notify()
File "C:\Program Files (x86)\Python37-32\lib\threading.py", line 348, in notify
waiters_to_notify = _deque(_islice(all_waiters, n))
File "C:\Users\mick\programming\git\nvda\source\comtypesMonkeyPatches.py", line 101, in newCpbDel
_cpbDel(self)
File "C:\Users\mick\programming\git\nvda\include\comtypes\comtypes\__init__.py", line 918, in __del__
self.Release()
File "C:\Users\mick\programming\git\nvda\include\comtypes\comtypes\__init__.py", line 1172, in Release
return self.__com_Release()
File "C:\Users\mick\programming\git\nvda\source\comtypesMonkeyPatches.py", line 26, in __call__
return super().__call__(*args,**kwargs)
At the same time, NVDA also deadlocks in its keydown hook, again in Queue.put on the same queue:
Python stack for thread 7632:
File "C:\Program Files (x86)\Python37-32\lib\threading.py", line 890, in _bootstrap
self._bootstrap_inner()
File "C:\Program Files (x86)\Python37-32\lib\threading.py", line 926, in _bootstrap_inner
self.run()
File "C:\Program Files (x86)\Python37-32\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\mick\programming\git\nvda\source\winInputHook.py", line 79, in hookThreadFunc
while windll.user32.GetMessageW(byref(msg),None,0,0):
File "C:\Users\mick\programming\git\nvda\source\winInputHook.py", line 54, in keyboardHook
if not keyDownCallback(kbd.vkCode,kbd.scanCode,bool(kbd.flags&LLKHF_EXTENDED),bool(kbd.flags&LLKHF_INJECTED)):
File "C:\Users\mick\programming\git\nvda\source\keyboardHandler.py", line 199, in internal_keyDownEvent
inputCore.manager.executeGesture(gesture)
File "C:\Users\mick\programming\git\nvda\source\inputCore.py", line 442, in executeGesture
queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech)
File "C:\Users\mick\programming\git\nvda\source\queueHandler.py", line 37, in queueFunction
queue.put_nowait((func,args,kwargs))
File "C:\Program Files (x86)\Python37-32\lib\queue.py", line 190, in put_nowait
return self.put(item, block=False)
File "C:\Program Files (x86)\Python37-32\lib\queue.py", line 132, in put
with self.not_full:
File "C:\Program Files (x86)\Python37-32\lib\threading.py", line 241, in __enter__
return self._lock.__enter__()
And finally also at the same time in NVDA's main thread:
Python stack for thread 1836:
File "source/nvda.pyw", line 215, in <module>
File "C:\Users\mick\programming\git\nvda\source\core.py", line 545, in main
app.MainLoop()
File "C:\Users\mick\programming\git\nvda\include\wxPython\wx\core.py", line 2134, in MainLoop
rv = wx.PyApp.MainLoop(self)
File "C:\Users\mick\programming\git\nvda\source\gui\__init__.py", line 1049, in Notify
self.run()
File "C:\Users\mick\programming\git\nvda\source\core.py", line 515, in run
queueHandler.pumpAll()
File "C:\Users\mick\programming\git\nvda\source\queueHandler.py", line 83, in pumpAll
flushQueue(eventQueue)
File "C:\Users\mick\programming\git\nvda\source\queueHandler.py", line 46, in flushQueue
if not queue.empty():
File "C:\Program Files (x86)\Python37-32\lib\queue.py", line 107, in empty
with self.mutex:
Python issue 14976: https://bugs.python.org/issue14976
Suggests that __del__ can run in Queue.put.
To work around this, where ever we only need an unbounded queue with no task management (such as our main event queue) we should use queue.SimpleQueue instead, as it does not allow the garbage collector or other signals to run inside put.
Steps to reproduce:
Case 1
Actual behavior:
NVDA eventually freezes.
Expected behavior:
NVDA should not freeze.
technical info
It seems that the Python garbage collector is allowed to run within Queue.put.
This then means that comtypes might call Release on a COM object in a del method, which in tern may cause a dead lock between NvDA and NVDAhelper in-process code in an app like Microsoft Word, when handling typed characters.
See this Python stack:
At the same time, NVDA also deadlocks in its keydown hook, again in Queue.put on the same queue:
And finally also at the same time in NVDA's main thread:
Python issue 14976: https://bugs.python.org/issue14976
Suggests that
__del__can run in Queue.put.To work around this, where ever we only need an unbounded queue with no task management (such as our main event queue) we should use queue.SimpleQueue instead, as it does not allow the garbage collector or other signals to run inside put.