After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 313523 - Deadlock in atk-bridge/bridge.c when global event listener triggered in non-main thread holding GDK threads lock
Deadlock in atk-bridge/bridge.c when global event listener triggered in non-m...
Status: RESOLVED FIXED
Product: at-spi
Classification: Platform
Component: atkbridge
1.6.x
Other Linux
: Normal major
: ---
Assigned To: bill.haneman
bill.haneman
Depends on:
Blocks:
 
 
Reported: 2005-08-15 14:32 UTC by Ed Catmur
Modified: 2006-06-14 15:08 UTC
See Also:
GNOME target: ---
GNOME version: 2.11/2.12


Attachments
Test case (deadlock.c) (998 bytes, text/plain)
2005-08-15 14:39 UTC, Ed Catmur
Details

Description Ed Catmur 2005-08-15 14:32:42 UTC
If a non-main thread is holding the GDK threads lock and triggers an atk-bridge
global event listener (e.g. by invoking atk_object_set_role on an AtkObject)
then atk-bridge deadlocks trying to communicate (by CORBA) with the main thread.

This was first observed in gnome-translate
http://www.nongnu.org/libtranslate/gnome-translate/ (the author calls
eel_alert_dialog_new () while holding GDK thread lock in a non-main thread); a
simplified testcase follows.
Comment 1 bill.haneman 2005-08-15 14:34:01 UTC
non-main threads shouldn't hold a GDK threads lock, should they?  Seems like
NOTABUG.
Comment 2 Ed Catmur 2005-08-15 14:39:22 UTC
Created attachment 50724 [details]
Test case (deadlock.c)

Compile with:

gcc -g -o deadlock deadlock.c $(pkg-config --cflags --libs libgnomeui-2.0
libgnome-2.0 gdk-2.0 gtk+-2.0 atk gthread-2.0)
Comment 3 Ed Catmur 2005-08-15 14:42:13 UTC
Re comment 1: the GDK docs say:

GTK+ is "thread aware" but not thread safe — it provides a global lock
controlled by gdk_threads_enter()/gdk_threads_leave() which protects all use of
GTK+. That is, only one thread can use GTK+ at any given time.
...
Before calling gdk_threads_leave() from a thread other than your main thread,
you probably want to call gdk_flush() to send all pending commands to the
windowing system.

The docs also have an example program which calls gtk+ functions from non-main
threads, protected by gdk_threads_enter()/gdk_threads_leave().
Comment 4 Ed Catmur 2005-08-15 14:43:24 UTC
Sorry, link: http://developer.gnome.org/doc/API/2.0/gdk/gdk-Threads.html
Comment 5 Ed Catmur 2005-08-15 14:52:22 UTC
Continuing with analysis: when atk_object_set_role() is called the global event
listener spi_atk_bridge_property_event_listener() is called, chaining to
spi_atk_emit_eventv() (both in atk-bridge/bridge.c).

spi_atk_emit_eventv() creates a SpiAccessible on the object, then later frees it
with Accessibility_Accessible_unref().

Accessibility_Accessible_unref is defined to Bonobo_Unknown_unref(), which is an
ORBit C stub; this calls ORBit_c_stub_invoke() -> ORBit_small_invoke_stub_n() ->
ORBit_small_invoke_stub(), which queues the request and waits for a reply with
giop_recv_buffer_get(), which deadlocks in a g_cond_wait() as the main thread is
waiting on the GDK threads lock.

Backtrace:

Thread 2 (Thread -1239004240 (LWP 8322))

  • #0 __kernel_vsyscall
  • #1 pthread_cond_wait
    from /lib/libpthread.so.0
  • #2 giop_recv_buffer_get
    from /usr/lib/libORBit-2.so.0
  • #3 ORBit_small_invoke_stub
    from /usr/lib/libORBit-2.so.0
  • #4 ORBit_small_invoke_stub_n
    from /usr/lib/libORBit-2.so.0
  • #5 ORBit_c_stub_invoke
    from /usr/lib/libORBit-2.so.0
  • #6 Bonobo_Unknown_unref
    from /usr/lib/libbonobo-activation.so.4
  • #7 spi_atk_emit_eventv
    from /usr/lib/gtk-2.0/modules/libatk-bridge.so
  • #8 spi_atk_bridge_signal_listener
    from /usr/lib/gtk-2.0/modules/libatk-bridge.so
  • #9 signal_emit_unlocked_R
    from /usr/lib/libgobject-2.0.so.0

Comment 6 Ed Catmur 2005-08-15 14:56:22 UTC
Confirmation is that if in spi_atk_emit_eventv() the
Accessibility_Accessible_unref() is changed to bonobo_object_unref(), the
deadlock disappears. 

I would suggest this as a patch, but I don't know enough about why
Accessibility_Accessible_unref() was used instead of bonobo_object_unref() in
the first place. (Note: another fix that works is to call
Accessibility_Accessible_unref() in an idle handler.)
Comment 7 bill.haneman 2005-08-15 15:16:49 UTC
non-main threads probably should never use ATK, since ATK is basically GUI stuff.
Comment 8 Ed Catmur 2005-08-15 17:02:21 UTC
But since control flow can enter ATK from any number of points (thanks to the
atk-bridge global listeners), avoiding ATK in non-main threads pretty much means
avoiding doing any GTK work; whereas the docs on gdk threading specifically say
that GDK is designed to allow (lock-protected) multi-thread GUI work. And GTK
_does_ work, except for this bug.
Comment 9 bill.haneman 2005-08-15 17:07:21 UTC
Ed: I don't understand what you mean when you assert that control flow can enter
ATK from outside the GDK event thread via the global listeners.  Those listeners
should only be interacting with the GDK event thread.
Comment 10 Ed Catmur 2005-08-15 17:39:18 UTC
Well, as well as the above atk_object_set_role() call, I'm also getting
spi_atk_bridge_signal_listener() (with a GailWindow:bounds-changed),
spi_atk_bridge_state_event_listener() (with a GailWindow:state-changed:showing),
etc, all happening outside the main thread. 

(These might be happening from a recursive GTK main loop - I'll need to
recompile GLib/GTK+ with symbols and without optimisations to check - but again
the docs do not prohibit calling a recursive main loop in a non-main thread.)
Comment 11 Ed Catmur 2005-09-05 13:48:19 UTC
re comment 9: control can enter ATK from almost any GTK function, since ATK
holds global listeners on ATK properties, signals and events that are triggered
by changes to the underlying GTK objects.

GTK functions can be called by any thread that has the GDK thread lock.

Thus control can enter ATK from any thread that has the GDK thread lock.
Comment 12 bill.haneman 2006-06-14 14:56:29 UTC
This was fixed by Michael in version 1.86 of at-spi/atk-bridge/bridge.c.
Comment 13 Ed Catmur 2006-06-14 15:08:06 UTC
Yes, the testcase is fixed. Thanks.