10
22
#: How long to wait for the core to be alive under normal circumstances
11
23
NORMAL_CORE_ALIVE_TIMEOUT=10
12
24
#: The minimum time to wait for the core to be alive
13
MIN_CORE_ALIVE_TIMEOUT=0.3
25
MIN_CORE_ALIVE_TIMEOUT=0.5
14
26
#: How long to wait between recovery attempts
15
27
RECOVER_ATTEMPT_INTERVAL = 0.05
28
#: The amount of time before the core should be considered severely frozen and a warning logged.
29
FROZEN_WARNING_TIMEOUT = 15
17
31
safeWindowClassSet=set([
18
32
'Internet Explorer_Server',
53
72
if _coreAliveEvent.isSet() or _shouldRecoverAfterMinTimeout():
74
if log.isEnabledFor(log.DEBUGWARNING) and not _coreAliveEvent.isSet():
75
log.debugWarning("Trying to recover from freeze, core stack:\n%s"%
76
"".join(traceback.format_stack(sys._current_frames()[_coreThreadID])))
56
78
while not _coreAliveEvent.isSet():
80
if curTime-lastTime>FROZEN_WARNING_TIMEOUT:
82
log.warning("Core frozen in stack:\n%s"%
83
"".join(traceback.format_stack(sys._current_frames()[_coreThreadID])))
57
84
# The core is dead, so attempt recovery.
58
85
isAttemptingRecovery = True
68
95
def _shouldRecoverAfterMinTimeout():
69
96
info=winUser.getGUIThreadInfo(0)
97
if not info.hwndFocus:
98
# The foreground thread is frozen or there is no foreground thread (probably due to a freeze elsewhere).
100
# Import late to avoid circular import.
102
#If a system menu has been activated but NVDA's focus is not yet in the menu then use min timeout
103
if info.flags&winUser.GUI_SYSTEMMENUMODE and info.hwndMenuOwner and api.getFocusObject().windowClassName!='#32768':
70
105
if winUser.getClassName(info.hwndFocus) in safeWindowClassSet:
72
107
if not winUser.isDescendantWindow(info.hwndActive, api.getFocusObject().windowHandle):
73
108
# The foreground window has changed.
75
newHwnd=info.hwndFocus if info.hwndFocus else info.hwndActive
110
newHwnd=info.hwndFocus
76
111
newThreadID=winUser.getWindowThreadProcessID(newHwnd)[1]
77
112
return newThreadID!=api.getFocusObject().windowThreadID
81
116
oledll.ole32.CoCancelCall(_coreThreadID,0)
120
NVDAHelper.localLib.cancelSendMessage()
122
@ctypes.WINFUNCTYPE(ctypes.wintypes.LONG, ctypes.c_void_p)
123
def _crashHandler(exceptionInfo):
124
# An exception might have been set for this thread.
125
# Clear it so that it doesn't get raised in this function.
126
ctypes.pythonapi.PyThreadState_SetAsyncExc(threading.currentThread().ident, None)
129
return 1 # EXCEPTION_EXECUTE_HANDLER
131
@ctypes.WINFUNCTYPE(None)
132
def _notifySendMessageCancelled():
133
caller = inspect.currentframe().f_back
136
# Set a profile function which will raise an exception when returning from the calling frame.
137
def sendMessageCallCanceller(frame, event, arg):
139
# Raising an exception will also cause the profile function to be deactivated.
141
sys.setprofile(sendMessageCallCanceller)
143
RPC_E_CALL_CANCELED = -2147418110
144
_orig_COMError_init = comtypes.COMError.__init__
145
def _COMError_init(self, hresult, text, details):
146
if hresult == RPC_E_CALL_CANCELED:
148
_orig_COMError_init(self, hresult, text, details)
86
151
"""Initialize the watchdog.
117
190
def __exit__(self,*args):
118
191
_resumeEvent.set()
193
class CancellableCallThread(threading.Thread):
194
"""A worker thread used to execute a call which must be made cancellable.
195
If the call is cancelled, this thread must be abandoned.
199
super(CancellableCallThread, self).__init__()
201
self._executeEvent = threading.Event()
202
self._executionDoneEvent = ctypes.windll.kernel32.CreateEventW(None, False, False, None)
205
def execute(self, func, args, kwargs, pumpMessages=True):
206
# Don't even bother making the call if the core is already dead.
207
if isAttemptingRecovery:
212
self._kwargs = kwargs
214
self._exc_info = None
215
self._executeEvent.set()
218
waitHandles = (ctypes.wintypes.HANDLE * 1)(self._executionDoneEvent)
219
waitIndex = ctypes.wintypes.DWORD()
220
timeout = int(1000 * CHECK_INTERVAL)
224
oledll.ole32.CoWaitForMultipleHandles(0, timeout, 1, waitHandles, ctypes.byref(waitIndex))
229
if windll.kernel32.WaitForSingleObject(self._executionDoneEvent, timeout) != winKernel.WAIT_TIMEOUT:
231
if isAttemptingRecovery:
232
self.isUsable = False
237
raise exc[0], exc[1], exc[2]
241
comtypes.CoInitializeEx(comtypes.COINIT_MULTITHREADED)
243
self._executeEvent.wait()
244
self._executeEvent.clear()
246
self._result = self._func(*self._args, **self._kwargs)
248
self._exc_info = sys.exc_info()
249
ctypes.windll.kernel32.SetEvent(self._executionDoneEvent)
250
ctypes.windll.kernel32.CloseHandle(self._executionDoneEvent)
252
cancellableCallThread = None
253
def cancellableExecute(func, *args, **kwargs):
254
"""Execute a function in the main thread, making it cancellable.
255
@param func: The function to execute.
257
@param ccPumpMessages: Whether to pump messages while waiting.
258
@type ccPumpMessages: bool
259
@param args: Positional arguments for the function.
260
@param kwargs: Keyword arguments for the function.
261
@raise CallCancelled: If the call was cancelled.
263
global cancellableCallThread
264
pumpMessages = kwargs.pop("ccPumpMessages", True)
265
if not isRunning or not _resumeEvent.isSet() or not isinstance(threading.currentThread(), threading._MainThread):
266
# Watchdog is not running or this is a background thread,
267
# so just execute the call.
268
return func(*args, **kwargs)
269
if not cancellableCallThread or not cancellableCallThread.isUsable:
270
# The thread hasn't yet been created or is not usable.
272
cancellableCallThread = CancellableCallThread()
273
cancellableCallThread.start()
274
return cancellableCallThread.execute(func, args, kwargs, pumpMessages=pumpMessages)
276
def cancellableSendMessage(hwnd, msg, wParam, lParam, flags=0, timeout=60000):
277
"""Send a window message, making the call cancellable.
278
The C{timeout} and C{flags} arguments should usually be left at their default values.
279
The call will still be cancelled if appropriate even if the specified timeout has not yet been reached.
280
@raise CallCancelled: If the call was cancelled.
283
result = ctypes.wintypes.DWORD()
284
NVDAHelper.localLib.cancellableSendMessageTimeout(hwnd, msg, wParam, lParam, flags, timeout, ctypes.byref(result))