AT-SPI caching and D-Bus usage

Mike Gorse <[email protected]> Mon, 7 Oct 2013 15:45:21 -0400 (EDT)
Newsgroups gmane.comp.gnome.accessibility.devel
Message-ID <[email protected]>
AT-SPI was originally designed around CORBA, specifically ORBit. Its use 
led to a large amount of inter-process communication. Method calls in 
ORBit were fairly quick, so this was not a huge problem, although that 
isn't to say that there were never performance issues. However, ORBit was 
deprecated for GNOME 3, and Codethink undertook an investigation of the 
feasibility of porting AT-SPi to D-Bus. Note that the D-Bus libraries were 
not really designed to be used in cases where a large amount of 
synchronous method calls are needed. Nevertheless, Codethink undertook an 
investigation of the feasibility of porting AT-SPI to D-Bus. Their 
main conclusions were that, although D-Bus method calls are slower than 
the equivalent CORBA calls, a lot of AT-SPI traffic generated by Orca 
comes from a handful of method calls--calls to fetch an object's name, 
parent, children, or state set, for instance. If these data could be 
cached, then a significant amount of traffic would become unnecessary.

This led to the current design, where some data are cached, and the 
default assumption is that applications will send notifications when an 
object's name, description, state, or children are changed. Calls 
requesting other data, such as attributes or screen coordinates, result in 
method calls. This design has a few problems:

- It relies on applications sending notifications any time an object's 
states or children change. If an application fails to do this, then Orca 
will see stale data, unless it has instructed AT-SPI not to cache certain 
kinds of data for the given application. It is sometimes difficult for a 
toolkit implementor to send notifications for every such change, and the 
result is that AT-SPI2 is made fragile in a way that was not previouisly 
the case.

- Not all data that an AT may need is cached. For instance, when Orca sees 
an event, it wants to look at the attributes for the accessible that 
triggered the event, in order to determine which toolkit sent it, in order 
to decide which script to use. Attributes are not cached, so this means 
that Orca always makes a round-trip D-Bus call to retrieve the attributes 
of the object that just sent the event. Similarly, when building a flat 
review context, it wants to know the extents of various accessibles on the 
screen, and extents are not cached, so a significant number of round-trip 
calls are needed in order to fetch extents for many different objects. One 
solution would be to add more things to the data that AT-SPI caches (ie, 
http://bugzilla.gnome.org/show_bug.cgi?id=649771), but this has the 
downside of making AT-SPI even more reliant on applications to send events 
when various things change, introducing the possibility for new bugs.

- Sometimes, it is necessary to avoid synchronous D-Bus calls altogether 
(see https://bugzilla.gnome.org/show_bug.cgi?id=708387).

I think that a good solution would be two-fold:

- Allow an event listener to request particular data be sent along with an 
event. For instance, Orca could request that an object's attributes be 
sent. The attributes would be cached probably for the duration of the 
event callback. While we're modifying the API for event listeners, it 
would be good to take a look at 
https://bugzilla.gnome.org/show_bug.cgi?id=640440.

- Add a function to allow various data to be fetched in one go. It would 
take a callback to be called whtn the data is ready. For instance, perhaps 
orca would want to know the roles, states, and extents of all 
(non-transient) objects descending from a particular accessible. All of 
this data would be stored in a cache that would be valid for the duration 
of the callback(?). If, say, atspi_accessible_get_attributes is called 
from within the callback and the attributes have been pre-fetched, then 
return the attributes we have cached rather than make another D-Bus call. 
In the long term, this might be able to replace the current caching 
mechanism. Ie, something like:

typedef void (*AtspiQueryCallback) (void *user_data)

void atspi_query_accessible (AtspiAccessible *obj,
                              const gchar **properties,
                              AtspiQueryCallback *cb,
                              void *user_data,
                              gboolean allow_sync,
                              gboolean include_descendants,
                              GError **error);

If allow_sync is FALSE, then AT-SPI will throw an exception rather than 
make a synchronous call. This could be used when it is desirable to 
guarantee that no synchronous calls are made (probably anything running 
inside gnome-shell would want this, since otherwise it can deadlock if it 
queries an application which is in turn making a non-AT-SPI-related D-Bus 
call to gnome-shell).

I'm not sure if having an "include descendants" parameter is best, or  if 
something more fine-grained would be better (perhaps an enum allowing just 
the accessible, the accesible and its children, or the accessible and all 
non-transient descendants?) Also, maybe we want a parameter to specify the 
starting point--it might be useful to be able to specify an ancestor with 
a given role as a starting point? This gets into whether it might be 
useful to take a string parameter specifying a predicate. It may be worth 
investigating whether xpath would be helpful / whether there are libraries 
that would be useful in terms of parsing it.

Comments welcome.

-Mike