Re: libatspi and event processing



Hey, thank Tou for quick answer.

On 02.03.2015 18:39, Alejandro Piñeiro wrote:
On 02/03/15 18:08, Lukasz Stanislawski wrote:
Hello everyone,

Currently, I'm developing small C application using libatspi and
encountered some problems with event processing.

What I have is two event listeners A and B registered with
atspi_event_listener_register() on different at-spi events. Inside
main loop assume that A listener can invoke callback a() on some atspi
event. Inside a() callback there are some calls to
atspi_accessible_get_* functions.

What I have noticed is that calling functins like.
atspi_accessible_get_name() triggers further dbus events processing
what result in different listeners to be invoked.

Those get functions need to trigger those dbus events, as the
information is usually not stored (except if it is already cached) at
libatspi. It needs to be requested to the client (the application). So
it is not possible to avoid that dbus triggering, as is the only way to
get the info you request.

Yeah, it is not possible to avoid dbus triggering when method is invoked, however is it possible not to process mismatching events? (eg. whem atspi_accessibile_get_name method is invoked only dbus message matching returning name will be processed) Can rest of events gathered while waiting for answer be queued and processed when main loop enters idle state?

As the result code of b() callback can be invoked (and sometimes is in
my application) during execution of a() callback code.called

About getting the other callback called because a different listener is
invoked: it is complicated to understand your problem at a so abstract
level. Could you give a example of what event is triggered when
atspi_accessible_get_<something> that invokes the second callback?

My real life example is a bit different and more complex however I will try to explain it in details. I have an accessibility application allowing users to navigate only over specific type of widgets etc, with some various policies.

To do this, first of all I have to be able to track what window have currently input focus. I'm doing it by registering listener on "window:active" and "object:state-changed:focused". When any of these events occurs on_window_activate callback is called.

Secondly. when window receives focus I have to gather all interesting information from accessibility tree and build my own cache from data that is not cached by atspi like positions and sizes. It occurred that there are some performance issues with it, (main loop is blocked too long not allowing other events to be processed), so I have decided to fetch data partially using idlers.

When my own data cache is done I start to process it and through the processing of data a call to atspi_accessibile_get_role is made. This causes the issues which I have mentioned earlier. When new "window:active" signal was send, it will process it and call on_window_activate callback which in my code - invalidates my cache.

When atspi_accessible_get_role returns my cache is already invalid. This resulted in cache errors and I couldn't find out what is happening unless i have looked into atspi source code.

This situations mostly happens when application right after gaining focus on window creates new window and focuses. This is a result backtrace, please check frame #14, #9 and #0 :

#0 on_window_activate (data=0x0, window=0xaf560)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/navigator.c:822
#1 0x0000e4f4 in _on_atspi_window_cb (event=0xd1ff8)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/window_tracker.c:18
#2 0xb6d9a638 in remove_datum (event=<optimized out>,
user_data=<optimized out>) at atspi-event-listener.c:56
#3 0xb6d9afd0 in _atspi_send_event (e=0xbefff8d8)
at atspi-event-listener.c:849
#4 0xb6d9b1c2 in _atspi_dbus_handle_event (bus=<optimized out>,
message=<optimized out>, data=<optimized out>)
at atspi-event-listener.c:979
#5 0xb6d9d2e0 in process_deferred_message (closure=0xc65a8)
at atspi-misc.c:748
#6 process_deferred_messages () at atspi-misc.c:787
#7 process_deferred_messages () at atspi-misc.c:777
#8 0xb6d9d6f2 in _atspi_dbus_call (obj=<optimized out>,
interface=<optimized out>, method=0xb6da4fc0 "GetRole", error=0x0,
type=0xb6da4fc8 "=>u") at atspi-misc.c:1152
#9 0xb6d98c3a in atspi_accessible_get_role (obj=0xc2718,
error=<optimized out>) at atspi-accessible.c:473
#10 0x0000dae0 in _filter_role_cb (container=<optimized out>,
data=<optimized out>, fdata=0x_on_cache_buildedc2718)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/flat_navi.c:153
--Type <return> to continue, or q <return> to quit--
#11 0x0000da92 in _accessible_list_split_with_filter (list=0x5a8e8,
cb=0xdac5 <_filter_role_cb>, user_data=0x11e0c <interesting_roles>)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/flat_navi.c:112
#12 0x0000dc34 in _flat_review_candidates_get (root=<optimized out>)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/flat_navi.c:213
#13 flat_navi_context_create (root=<optimized out>)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/flat_navi.c:257
#14 0x0000fa4e in _on_cache_builded (data=<optimized out>)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/navigator.c:802
#15 0x0000e878 in _do_cache (data=<optimized out>)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/object_cache.c:173
#16 _do_cache (data=<optimized out>)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/object_cache.c:163
#17 0xb6e05860 in _ecore_call_task_cb (data=<optimized out>,
func=<optimized out>) at ecore_private.h:272
#18 _ecore_idler_all_call () at ecore_idler.c:119
#19 0xb6e067c6 in _ecore_main_loop_spin_core () at ecore_main.c:1746
#20 0xb6e069d8 in _ecore_main_loop_spin_timers () at ecore_main.c:1780
#21 _ecore_main_loop_iterate_internal (once_only=0) at ecore_main.c:1903
#22 0xb6e06dc0 in ecore_main_loop_begin () at ecore_main.c:956
#23 0xb6fa5ab6 in appcore_efl_main () from /usr/lib/libappcore-efl.so.1
#24 0x0000bfcc in main (argc=1, argv=0xbefffdc4)
at /usr/src/debug/org.tizen.smart-navigator-0.0.1/src/main.c:71

I have managed to solve my issues by simple removing all calls to atspi_accessible_get_* in my cache processing code. However I was carious if I'm doing something wrong or just atspi programmer have to be aware that this can happen. Maybe shouldn't it be better if it worked as I wrote in first at the begining, What do You think?


I find this very inconvenient because every atspi_accessible_get_*
related call in callback a() have to be wrapped with some 'rollback'
logic in my application, because events of listener B can occur.

So my questions are:

1. What is a preferred way of handling such cases? I want every
callback to process sequentially and not to introduce new race
conditions in my application. Such APIs makes my code hard to write
because "everything can happen everywhere".

As mentioned, if in your callback you call new atspi methods, they would
need to use dbus to get that info. But it would be good to have a real
example in order to know why this is problematic.

2. Shouldn't it be better to dispatch all deferred events when main
loop enters idle state? Mayby there is a reasoning for such design
that I'm not familiar of.

You seem to suggest the addition of an asynchronous API. If that is the
case, it is true that there are some attempts to include an asynchronous
API in order to solve some issues of the classic libatspi synchronous
API. But in most cases, libatspi is still an asynchronous API.

BR



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