Skip to content

Commit

Permalink
Finally, have proper reconnect
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonarddeR committed Jul 29, 2023
1 parent 0d2bbf4 commit ca670bc
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 24 deletions.
2 changes: 0 additions & 2 deletions addon/brailleDisplayDrivers/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ def _command_executeGesture(self, payload: bytes):
log.error("Unexpected NoInputGestureAction", exc_info=True)

def display(self, cells: List[int]):
#if len(cells) == 0:
# return
# cells will already be padded up to numCells.
arg = bytes(cells)
self.writeMessage(protocol.BrailleCommand.DISPLAY, arg)
Expand Down
1 change: 0 additions & 1 deletion addon/globalPlugins/rdAccess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import globalPluginHandler
import addonHandler
import hwIo
from . import directoryChanges, settingsPanel
import typing
from fnmatch import fnmatch
Expand Down
2 changes: 0 additions & 2 deletions addon/globalPlugins/rdAccess/secureDesktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import os.path
import braille
import synthDriverHandler
from ctypes import WinError
import weakref
from logHandler import log

if typing.TYPE_CHECKING:
from ...lib import ioThreadEx
Expand Down
1 change: 0 additions & 1 deletion addon/lib/driver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from .settingsAccessor import SettingsAccessorBase
import sys
from baseObject import AutoPropertyObject
import time
from utils.security import post_sessionLockStateChanged, isRunningOnSecureDesktop

ERROR_INVALID_HANDLE = 0x6
Expand Down
49 changes: 39 additions & 10 deletions addon/lib/ioThreadEx.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from ctypes import POINTER, WINFUNCTYPE, WinError, addressof, byref, cast, windll
from ctypes.wintypes import BOOL, BOOLEAN, DWORD, HANDLE, LPVOID, LPHANDLE
# RDAccess: Remote Desktop Accessibility for NVDA
# Copyright 2023 Leonard de Ruijter <[email protected]>
# License: GNU General Public License version 2.0

from ctypes import POINTER, WINFUNCTYPE, WinError, addressof, byref, windll
from ctypes.wintypes import BOOL, BOOLEAN, DWORD, HANDLE, LPVOID
from inspect import ismethod
import threading
from typing import Callable, Dict, Tuple, Union
Expand All @@ -25,15 +29,25 @@
]
]
windll.kernel32.RegisterWaitForSingleObject.restype = BOOL
windll.kernel32.RegisterWaitForSingleObject.argtypes = (POINTER(HANDLE), HANDLE, WaitOrTimerCallback, LPVOID, DWORD, DWORD)
windll.kernel32.RegisterWaitForSingleObject.argtypes = (
POINTER(HANDLE),
HANDLE,
WaitOrTimerCallback,
LPVOID,
DWORD,
DWORD
)


class IoThreadEx(hwIo.ioThread.IoThread):
_waitOrTimerCallbackStore: WaitOrTimerCallbackStoreT = {}

@WaitOrTimerCallback
def _internalWaitOrTimerCallback(param: WaitOrTimerCallbackIdT, timerOrWaitFired: bool):
(threadIdent, waitObject, reference, actualParam) = IoThreadEx._waitOrTimerCallbackStore.pop(param, (0, 0, None, 0))
(threadIdent, waitObject, reference, actualParam) = IoThreadEx._waitOrTimerCallbackStore.pop(
param,
(0, 0, None, 0)
)
threadInst: IoThreadEx = threading._active.get(threadIdent)
if not isinstance(threadInst, IoThreadEx):
log.error(f"Internal WaitOrTimerCallback called from unknown thread")
Expand All @@ -45,14 +59,18 @@ def _internalWaitOrTimerCallback(param: WaitOrTimerCallbackIdT, timerOrWaitFired
function = reference()
if not function:
log.debugWarning(
f"Not executing queued WaitOrTimerCallback {param}:{reference.funcName} with param {actualParam} because reference died"
f"Not executing queued WaitOrTimerCallback {param}:{reference.funcName} with param {actualParam} "
"because reference died"
)
return

try:
function(actualParam, bool(timerOrWaitFired))
except Exception:
log.error(f"Error in WaitOrTimerCallback function {function!r} with id {param} queued to IoThread", exc_info=True)
log.error(
f"Error in WaitOrTimerCallback function {function!r} with id {param} queued to IoThread",
exc_info=True
)
finally:
threadInst.queueAsApc(threadInst._postWaitOrTimerCallback, waitObject)

Expand All @@ -73,9 +91,20 @@ def waitForSingleObjectWithCallback(

waitObject = HANDLE()
reference = BoundMethodWeakref(func) if ismethod(func) else AnnotatableWeakref(func)
reference.funcName = repr(func)
waitObjectAddr = addressof(waitObject)
self._waitOrTimerCallbackStore[waitObjectAddr] = (self.ident, waitObject, reference, param)
waitRes = windll.kernel32.RegisterWaitForSingleObject(byref(waitObject), objectHandle, self._internalWaitOrTimerCallback, waitObjectAddr, waitTime, flags)
if not waitRes:
raise WinError()
reference.funcName = repr(func)
try:
waitRes = windll.kernel32.RegisterWaitForSingleObject(
byref(waitObject),
objectHandle,
self._internalWaitOrTimerCallback,
waitObjectAddr,
waitTime,
flags
)
if not waitRes:
raise WinError()
except Exception:
del self._waitOrTimerCallbackStore[waitObjectAddr]
raise
11 changes: 4 additions & 7 deletions addon/lib/namedPipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from hwIo.ioThread import IoThread
from typing import Callable, Iterator, Optional, Union
from ctypes import (
WINFUNCTYPE,
byref,
c_ulong,
GetLastError,
Expand All @@ -15,7 +14,7 @@
windll,
WinError,
)
from ctypes.wintypes import BOOL, BOOLEAN, HANDLE, DWORD, LPCWSTR, LPVOID
from ctypes.wintypes import BOOL, HANDLE, DWORD, LPCWSTR
from serial.win32 import (
CreateFile,
ERROR_IO_PENDING,
Expand Down Expand Up @@ -203,13 +202,12 @@ def _handleConnect(self):
self._ioDone(error, 0, byref(ol))

def _handleConnectCallback(self, parameter: int, timerOrWaitFired: bool):
assert timerOrWaitFired == False
log.debug(f"Event set for {self.pipeName}")
numberOfBytes = DWORD()
log.debug(f"Getting overlapped result for {self.pipeName} after wait for event")
if not windll.kernel32.GetOverlappedResult(self._file, byref(self._connectOl), byref(numberOfBytes), False):
error = GetLastError()
log.debugWarning(f"Error while getting overlapped result for {self.pipeName}: {WinError(error)}")
log.debug(f"Error while getting overlapped result for {self.pipeName}: {WinError(error)}")
self._ioDone(error, 0, byref(self._connectOl))
return
self._connected = True
Expand All @@ -226,9 +224,8 @@ def _handleConnectCallback(self, parameter: int, timerOrWaitFired: bool):
self._initialRead()

def _onReadError(self, error: int):
import tones
tones.beep(440, 30)
winErr = WinError(error)
log.debug(f"Read error: {winErr}")
if isinstance(winErr, BrokenPipeError):
self.disconnect()
self._initialRead()
Expand All @@ -243,7 +240,7 @@ def _asyncRead(self, param: Optional[int] = None):
super()._asyncRead()

def disconnect(self):
if not windll.kernel32.DisconnectNamedPipe(self._file):
if not windll.kernel32.DisconnectNamedPipe(self._file):
raise WinError()
self._connected = False
self.pipeProcessId = None
Expand Down
5 changes: 4 additions & 1 deletion addon/lib/protocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,13 @@ def terminate(self):
superTerminate = getattr(super(), "terminate", None)
if superTerminate:
superTerminate()
# We must sleep before closing the connection as not doing this
# can leave a remote handler in a bad state where it can not be re-initialized.
time.sleep(self.timeout / 10)
finally:
self.terminateIo()
self._attributeValueProcessor.clearCache()
self._bgExecutor.shutdown(False)
self._bgExecutor.shutdown()

def _onReceive(self, message: bytes):
if self._receiveBuffer:
Expand Down

0 comments on commit ca670bc

Please sign in to comment.