From b6d725962ded1538a6b46f99feafe225f0c45a8b Mon Sep 17 00:00:00 2001 From: James Teh Date: Tue, 7 Dec 2010 16:28:40 +1000 Subject: [PATCH] AppModule now has a terminate() method instead of using __del__() to clean up. This is necessary because it is possible that some app modules might not be garbage collected until after NVDAHelper is terminated and app module termination depends on NVDAHelper still being initialised. All running app modules are explicitly terminated on exit before NVDAHelper terminates. Fixes many unlogged exceptions (resulting in a strange sound) when NVDA exits. Also, fix typo in appModuleHandler.update() which might have caused getAppModuleFromProcessID() to be called more than it should, although this wouldn't have been noticeable in reality. Fixes #1263. --- source/appModuleHandler.py | 27 ++++++++++++++++++++++----- source/core.py | 2 ++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/source/appModuleHandler.py b/source/appModuleHandler.py index b795e054c..6fb9ea647 100644 --- a/source/appModuleHandler.py +++ b/source/appModuleHandler.py @@ -94,17 +94,22 @@ def getAppModuleFromProcessID(processID): return mod def update(processID): - """Removes any appModules from te cache who's process has died, and also tries to load a new appModule for the given process ID if need be. + """Removes any appModules from the cache whose process has died, and also tries to load a new appModule for the given process ID if need be. @param processID: the ID of the process. @type processID: int """ for deadMod in [mod for mod in runningTable.itervalues() if not mod.isAlive]: log.debug("application %s closed"%deadMod.appName) - del runningTable[deadMod.processID]; + del runningTable[deadMod.processID] if deadMod in set(o.appModule for o in api.getFocusAncestors()+[api.getFocusObject()] if o and o.appModule): if hasattr(deadMod,'event_appLoseFocus'): - deadMod.event_appLoseFocus(); - getAppModuleFromProcessID(processID) + deadMod.event_appLoseFocus() + try: + deadMod.terminate() + except: + log.exception("Error terminating app module %r" % deadMod) + # This creates a new app module if necessary. + getAppModuleFromProcessID(processID) def doesAppModuleExist(name): return any(importer.find_module("appModules.%s" % name) for importer in _importers) @@ -147,6 +152,14 @@ def initialize(): config.addConfigDirsToPythonPackagePath(appModules) _importers=list(pkgutil.iter_importers("appModules.__init__")) +def terminate(): + for processID, app in runningTable.iteritems(): + try: + app.terminate() + except: + log.exception("Error terminating app module %r" % app) + runningTable.clear() + #base class for appModules class AppModule(baseObject.ScriptableObject): """Base app module. @@ -191,7 +204,11 @@ class AppModule(baseObject.ScriptableObject): def _get_isAlive(self): return bool(winKernel.waitForSingleObject(self.processHandle,0)) - def __del__(self): + def terminate(self): + """Terminate this app module. + This is called to perform any clean up when this app module is being destroyed. + Subclasses should call the superclass method first. + """ winKernel.closeHandle(self.processHandle) NVDAHelper.localLib.destroyConnection(self.helperLocalBindingHandle) diff --git a/source/core.py b/source/core.py index 623f5286e..4ea205ec8 100644 --- a/source/core.py +++ b/source/core.py @@ -306,6 +306,8 @@ This initializes all modules such as audio, IAccessible, keyboard, mouse, and GU JABHandler.terminate() except: log.error("Error terminating Java Access Bridge support",exc_info=True) + log.debug("Terminating app module handler") + appModuleHandler.terminate() log.debug("Terminating NVDAHelper") try: NVDAHelper.terminate() -- 2.11.0