[pyatspi2] Add thread safety around main loop invocations



commit a120558880f43f0be148ff3f2e8fc2ad779659ff
Author: Mike Gorse <mgorse novell com>
Date:   Fri Jul 30 10:48:53 2010 -0400

    Add thread safety around main loop invocations
    
    Avoid running a main loop in two separate threads.  Prevents deadlock in, ie,
    Strongwind tests with listeners that start the registry in a separate thread
    while making calls in the main thread.

 pyatspi/accessible.py     |    3 +++
 pyatspi/appevent.py       |    2 +-
 pyatspi/busutils/bus.py   |   11 ++++++-----
 pyatspi/busutils/proxy.py |   30 ++++++++++++++++++++++++------
 pyatspi/cache.py          |    4 ++--
 pyatspi/deviceevent.py    |    3 ++-
 pyatspi/factory.py        |    3 ++-
 pyatspi/registry.py       |   28 +++++++++++++++++++++++++++-
 pyatspi/selection.py      |    6 +++++-
 9 files changed, 72 insertions(+), 18 deletions(-)
---
diff --git a/pyatspi/accessible.py b/pyatspi/accessible.py
index 61515b6..0c7e633 100644
--- a/pyatspi/accessible.py
+++ b/pyatspi/accessible.py
@@ -428,6 +428,9 @@ class Accessible(BaseProxy):
                 @return : a StateSet encapsulating the currently true states
                 of the object.
                 """
+                if self.getRole() == 78:
+                        print "embedded"
+                        return self[0].getState()
                 if self.cached:
                         return _marshal_state_set(self._cached_data.state)
                 else:
diff --git a/pyatspi/appevent.py b/pyatspi/appevent.py
index fcc63be..304c585 100644
--- a/pyatspi/appevent.py
+++ b/pyatspi/appevent.py
@@ -306,7 +306,7 @@ class Event(object):
 class _ApplicationEventRegister (object):
 
         def __init__ (self, factory):
-                self._bus = AsyncAccessibilityBus ()
+                self._bus = AsyncAccessibilityBus (registry.Registry())
                 self._factory = factory
 
                 self._event_listeners = {}
diff --git a/pyatspi/busutils/bus.py b/pyatspi/busutils/bus.py
index ddbe42c..00652f7 100644
--- a/pyatspi/busutils/bus.py
+++ b/pyatspi/busutils/bus.py
@@ -24,7 +24,6 @@ import gobject
 from proxy import AccessibilityProxy
 
 import sys
-import traceback
 
 def _get_accessibility_bus_address ():
 
@@ -114,7 +113,7 @@ class AsyncAccessibilityBus (_AccessibilityBus):
 
 	_shared_instance = None
 	
-	def __new__ (cls):
+	def __new__ (cls, registry):
 		if AsyncAccessibilityBus._shared_instance:
 			return AsyncAccessibilityBus._shared_instance
 		else:
@@ -127,11 +126,12 @@ class AsyncAccessibilityBus (_AccessibilityBus):
 			
 			return AsyncAccessibilityBus._shared_instance
 
-	def __init__ (self):
+	def __init__ (self, registry):
 		try:
 			_AccessibilityBus.__init__ (self, _get_accessibility_bus_address(), None)
 		except Exception:
 			_AccessibilityBus.__init__ (self, _bus.BusConnection.TYPE_SESSION, None)
+                self.registry = registry
 
 class SyncAccessibilityBus (_bus.BusConnection):
 	"""
@@ -140,7 +140,7 @@ class SyncAccessibilityBus (_bus.BusConnection):
 
 	_shared_instance = None
 	
-	def __new__ (cls):
+	def __new__ (cls, registry):
 		if SyncAccessibilityBus._shared_instance:
 			return SyncAccessibilityBus._shared_instance
 		else:
@@ -153,8 +153,9 @@ class SyncAccessibilityBus (_bus.BusConnection):
 			
 			return SyncAccessibilityBus._shared_instance
 
-	def __init__ (self):
+	def __init__ (self, registry):
 		try:
 			_bus.BusConnection.__init__ (self, _get_accessibility_bus_address(), None)
 		except Exception:
 			_bus.BusConnection.__init__ (self, _bus.BusConnection.TYPE_SESSION, None)
+                self.registry = registry
diff --git a/pyatspi/busutils/proxy.py b/pyatspi/busutils/proxy.py
index 6f84160..9bff254 100644
--- a/pyatspi/busutils/proxy.py
+++ b/pyatspi/busutils/proxy.py
@@ -14,6 +14,7 @@
 
 import dbus.connection as _connection
 import gobject
+import threading
 
 import Queue
 
@@ -39,10 +40,15 @@ class AccessibilityProxy (_connection.ProxyObject):
 	_main_loop_pool = _MainLoopPool ()
 
         class DBusMethodCallbackData (object):
-                def __init__ (self):
+                def __init__ (self, proxy):
 			# Will raise the Empty exception if we have hit
 			# The re-entrancy limit
-			self.loop  = AccessibilityProxy._main_loop_pool.get_nowait ()
+                        if proxy._bus.registry.has_implementations == False or proxy._bus.registry.started == False or threading.currentThread() == proxy._bus.registry.thread:
+			        self.event = None
+			        self.loop  = AccessibilityProxy._main_loop_pool.get_nowait ()
+                        else:
+			        self.event = threading.Event()
+			        self.loop  = None
                         self.error = None
                         self.args  = None
         
@@ -58,14 +64,18 @@ class AccessibilityProxy (_connection.ProxyObject):
 			#print ("\t" * depth) + "Depth=" + str(gobject.main_depth())
 			#print
 
-                        data = AccessibilityProxy.DBusMethodCallbackData()
+                        self._bus.registry.acquireLock()
+                        data = AccessibilityProxy.DBusMethodCallbackData(self)
 
                         def method_error_callback (e):
                                 data.error = e
 				def main_quit ():
 					data.loop.quit()
 					return False
-				gobject.idle_add (main_quit)
+                                if data.event is not None:
+                                        data.event.set()
+                                else:
+				        gobject.idle_add (main_quit)
 
                         def method_reply_callback (*jargs):
 
@@ -79,7 +89,11 @@ class AccessibilityProxy (_connection.ProxyObject):
 				def main_quit ():
 					data.loop.quit()
 					return False
-				gobject.idle_add (main_quit)
+
+                                if data.event is not None:
+                                        data.event.set()
+                                else:
+				        gobject.idle_add (main_quit)
 
                         method (reply_handler=method_reply_callback,
                                 error_handler=method_error_callback,
@@ -87,9 +101,13 @@ class AccessibilityProxy (_connection.ProxyObject):
                                 **ikwargs)
 
 			self._bus.freezeEvents()
-			data.loop.run ()
+                        if data.event is not None:
+                                data.event.wait()
+                        else:
+			        data.loop.run ()
 			AccessibilityProxy._main_loop_pool.put_nowait (data.loop)
 			self._bus.thawEvents()
+                        self._bus.registry.releaseLock()
 
 			#depth = gobject.main_depth()
 			#print ("\t" * depth) + "Post-recurse"
diff --git a/pyatspi/cache.py b/pyatspi/cache.py
index 507a657..207a8d4 100644
--- a/pyatspi/cache.py
+++ b/pyatspi/cache.py
@@ -84,7 +84,7 @@ class DesktopCacheManager (object):
         """
  
         def __init__(self, cache):
-                bus = SyncAccessibilityBus ()
+                bus = SyncAccessibilityBus (registry.Registry())
 
                 self._cache = cache
                 self._application_list = {}
@@ -215,7 +215,7 @@ class ApplicationCacheManager (object):
                 """
                 # It is important that this bus is async as registered signals may
                 # come from orca itself.
-                bus = AsyncAccessibilityBus()
+                bus = AsyncAccessibilityBus(registry.Registry())
 
                 self._cache = cache
                 self._bus_name = bus_name
diff --git a/pyatspi/deviceevent.py b/pyatspi/deviceevent.py
index 719af11..081bc39 100644
--- a/pyatspi/deviceevent.py
+++ b/pyatspi/deviceevent.py
@@ -13,6 +13,7 @@
 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 from interfaces import *
+import registry
 
 import dbus as _dbus
 import dbus.service as _service
@@ -534,7 +535,7 @@ class KeyboardDeviceEventListener(_service.Object):
 class _DeviceEventRegister (object):
         
         def __init__ (self):
-                self._bus = SyncAccessibilityBus ()
+                self._bus = SyncAccessibilityBus (registry.Registry())
                 self.dev = DeviceEventController (self._bus)
                 self.deviceClients = {}
 
diff --git a/pyatspi/factory.py b/pyatspi/factory.py
index 9b31b15..1a4d783 100644
--- a/pyatspi/factory.py
+++ b/pyatspi/factory.py
@@ -13,6 +13,7 @@
 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 import interfaces
+import registry
 
 from accessible import *
 from action import *
@@ -47,7 +48,7 @@ class AccessibleFactory (object):
 
         def __init__ (self, cache):
 
-                self._connection = AsyncAccessibilityBus() 
+                self._connection = AsyncAccessibilityBus(registry.Registry())
 
                 self._interfaces = { 
                         interfaces.ATSPI_ACCESSIBLE:Accessible,
diff --git a/pyatspi/registry.py b/pyatspi/registry.py
index 4f58307..3d3eaf5 100644
--- a/pyatspi/registry.py
+++ b/pyatspi/registry.py
@@ -24,6 +24,8 @@
 import dbus
 import os as _os
 import Queue
+import threading
+import time
 import traceback
 
 from busutils import *
@@ -136,7 +138,7 @@ class Registry(object):
                 _os.environ["AT_SPI_CLIENT"] = "1"
 
                 # Set up the device event controllers
-                _connection = SyncAccessibilityBus ()
+                _connection = SyncAccessibilityBus (self)
                 _bus_object = _connection.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus")
 
                 if app_name:
@@ -174,8 +176,16 @@ class Registry(object):
                 """
                 if not self.has_implementations:
                         self._set_default_registry ()
+                self.acquireLock()
+                self.thread = threading.currentThread()
                 self.started = True
+
+                def idleReleaseLock():
+                        self.releaseLock()
+                        return False
+
                 try:
+                        gobject.idle_add(idleReleaseLock)
                         self.main_loop.run()
                 except KeyboardInterrupt:
                         pass
@@ -392,6 +402,22 @@ class Registry(object):
                         self._set_default_registry ()
                 self.device_event_register.generateMouseEvent (x, y, name)
 
+        def acquireLock(self):
+                """
+                Acquire a lock, creating the registry irst if needed.
+                """
+                try:
+                        self.lock.acquire()
+                except AttributeError:
+                        self.lock = threading.Lock()
+                        self.lock.acquire()
+
+        def releaseLock(self):
+                """
+                Release the lock
+                """
+                self.lock.release()
+
 #------------------------------------------------------------------------------
 
 def set_default_registry (main_loop, app_name=None):
diff --git a/pyatspi/selection.py b/pyatspi/selection.py
index ef8a25f..b52d962 100644
--- a/pyatspi/selection.py
+++ b/pyatspi/selection.py
@@ -119,8 +119,12 @@ class Selection(Accessible):
                 be selected.
                 @return True if the child was successfully selected, False otherwise.
                 """
+		print "calling selectChild"
                 func = self.get_dbus_method("SelectChild", dbus_interface=ATSPI_SELECTION)
-                return func(index)
+		print "calling"
+		val = func(index)
+		print "called"
+		return val
 
         def get_nSelectedChildren(self):
                 return dbus.Int32(self._pgetter(ATSPI_SELECTION, "NSelectedChildren"))



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]