Skip to content

NVDA may sometimes freeze within Queue.put #11369

@michaelDCurran

Description

@michaelDCurran

Steps to reproduce:

Case 1

  1. Configure NVDA to use a braille display, or open NVDA's Braille Viewer from the Tools menu.
  2. Open a new document in Microsoft Word.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions