Re: Emitting signals from threads
- From: Mitko Haralanov <voidtrance gmail com>
- To: Lex Trotman <elextr gmail com>, ML-gtk <gtk-list gnome org>
- Subject: Re: Emitting signals from threads
- Date: Wed, 9 Jan 2019 09:31:53 -0800
After some offline discussion, I decided to experiment with
g_idle_add_full() since that function seems to work for other people.
Unfortunately, it doesn't in my case. The width of the first column is
still 0 and bad behavior is reproducible:
column[0](0) = 0 -> 0
cell[0] = min->102, natural->102, text->98
column[1](66) = 98 -> 164
cell[0] = min->20, natural->20
cell[1] = min->16, natural->16
cell[2] = min->35, natural->35
column[2](36) = 164 -> 200
cell[0] = min->16, natural->16
x = 138.000000, y = 274.000000
Looking at the code for g_main_context_invoke_full() and
g_idle_add_full(), as expected, the portion of
g_main_context_invoke_full() that deals with the idle source is
virtually identical to g_idle_add_full(). So, the issue is somewhere
deeper.
Are there any GLib or Gtk+ developers on this list that can offer any help?
On Tue, Jan 8, 2019 at 4:35 PM Lex Trotman <elextr gmail com> wrote:
"calling gtk functions from a thread would be possible only through
g_idle_add()." thats my understanding from
https://developer.gnome.org/gdk3/stable/gdk3-Threads.html and thats
what our app does. It never seems to be a performance problem, the
main thread continuously chews its way through the idle queue.
Cheers
Lex
On Wed, 9 Jan 2019 at 10:17, Mitko Haralanov <voidtrance gmail com> wrote:
I havent looked at the actual code but I am willing to bet that the "idle source" is just using
g_idle_add(). If so, g_main_context_invoke_full() is essentially the same.
If not the case, calling gtk functions from a thread would be possible only through g_idle_add(). I would
think that if that were the case, it would have been mentioned in the docs.
Thank you,
Mitko
On Tue, Jan 8, 2019, 16:11 Lex Trotman <elextr gmail com wrote:
Hi Mitko,
Same problem, from g_main_context_invoke() "In any other case, an idle
source is created to call function and that source is attached to
context (presumably to be run in another thread)."
Cheers
Lex
On Wed, 9 Jan 2019 at 09:55, Mitko Haralanov <voidtrance gmail com> wrote:
Hi Lex,
That is for the "notify" callback, which only deals with free/release
of the "data" pointer. The "function" callback is different and should
be called from the context default thread (as the documentation
describes.)
Thank you,
Mitko
On Tue, Jan 8, 2019 at 3:44 PM Lex Trotman <elextr gmail com> wrote:
Mito,
It seems to me that since the g_main_context_invoke_full() docs say
"notify should not assume that it is called from any particular
thread" that notify cannot call GTK functions since GTK is not thread
safe.
Cheers
Lex
PS I'm not on gtk-list and am not gonna join for one line, feel free to forward
On Wed, 9 Jan 2019 at 04:03, Mitko Haralanov via gtk-list
<gtk-list gnome org> wrote:
Thanks for the reply.
When calling g_main_context_invoke_full(), I am using NULL as the
context pointer. According to the documentation of
g_main_context_invoke():
"If context is NULL then the global default main context — as
returned by g_main_context_default() — is used."
So, the code should be using the correct context.
One of the main reasons why I am not using g_idle_add() is the timing
of the callback. I don't want to defer the processing of the callback
to the "idle" time since this is signal handling related. Another
advantage of g_main_context_invoke[_full]() is that it will check the
"context" of the caller and, if possible, will call the callback
directly:
"If context is owned by the current thread, function is called
directly. Otherwise, if context is the thread-default main context of
the current thread and g_main_context_acquire()
succeeds, then function is called and g_main_context_release()
is called afterwards.
In any other case, an idle source is created to call function
and that source is attached to context (presumably to be run in
another thread). The idle source is attached with
G_PRIORITY_DEFAULT priority. If you want a different priority,
use g_main_context_invoke_full()."
Your suggested implementation is basically the GLib's async queues.
Or, may be, a custom event source. However, it seems that
g_main_context_invoke_full() should be doing exactly that. With all
due respect, I also don't see how it would solve the issue. If the
current implementation has an problem with locking/race conditions,
would your suggested implementation suffer from the same issue since
it's using the same or similar mechanism for calling the callback?
Thank you,
Mitko
On Mon, Jan 7, 2019 at 3:54 PM ente <ente ck76 de> wrote:
Hi,
I am not sure with my answer. Treat it as unreliable.
There seems to be a difference between g_idle_add
and g_main_context_invoke_full. While the documentation of idle_add
says:
"Adds a function to be called [...] to the default main loop." (i.e.
main thread / the only gtk-thread) g_main_context_invoke_full does not
mention the main loop - although it mentions a "context" which I have
no experience with and which may be related to the main loop. So maybe
you must retrieve the correct context using g_main_context_default or
g_main_context_acquire. Keep in mind: glib is thread safe, gtk is not.
g_main_context_invoke_full may support glib multi threading while
g_idle_add clearly sends your function call to the gtk thread.
The effect you are describing makes sense to me. The effects you
observe sound very much like race conditions in the treeview event
handler.
Why aren't you using g_idle_add in the first place? In my experience
this works like a charm. Be careful with one thing tho: The main loop
has a "todo-list". Each time you call "g_idle_add" it adds an item to
that todo-list and schedules a call to your function. If your function
doesn't return false, it won't even be taken down from the todo-list.
Each click event ends on that todo-list. Each column resize adds
multiple items to that todo-list (depending on your column resize
policy and the number of rows). As soon as you add more items to that
list, your UI becomes unresponsive. It seems advisable to build your
own shadow todo-list for the update process, i.e.:
* setup a function "update_progress"
* on the first threaded update event, call idle_add for the function
and put the new progress and the row number in your own data structure
* while the function is still "planned in for idle_add", just add new
events to your own data structure
* process 1 to 10 (maybe some more) events on each of the calls
* the function returns true as long as there is still some events in
your own list; it returns false otherwise
* apply mutex checks on your data structure (not sure if i should
mention, I am sure you had this in your mind already)
Each time you update a progress bar in your treeview, several (at least
one) events are added to the gtk-todo list (repaint!, re-order? and
maybe some more). Doing multiple progress events at once may lower the
amount of events, gtk has to process (a re-order affects all rows -
doing an update on multiple rows does not change anything here).
That's my 5 cents. I hope it helped a bit.
regards,
ente
On Mon, 2019-01-07 at 08:28 -0800, Mitko Haralanov via gtk-list wrote:
Anyone have any ideas? I still can't figure out why the column sizes
go to
0.
Thank you.
On Tue, Dec 18, 2018, 13:40 Mitko Haralanov <
voidtrance gmail com
wrote:
This is Gtk3:
gtk3-3.22.26-2.fc27.x86_64
On Tue, Dec 18, 2018 at 1:14 PM Luca Bacci via gtk-list <
gtk-list gnome org
wrote:
Is it Gtk2 or Gtk3, which version exactly?
Il giorno mar 18 dic 2018 alle ore 18:47 Mitko Haralanov via gtk-
list <
gtk-list gnome org
ha scritto:
I mistakenly replied only to Luca!! Forwarding to the list.
(Sorry, Luca, my bad)
- Mitko
---------- Forwarded message ---------
From: Mitko Haralanov <
voidtrance gmail com
Date: Tue, Dec 18, 2018 at 9:37 AM
Subject: Re: Emitting signals from threads
To: Luca Bacci <
luca bacci982 gmail com
I found something that is different between the two cases -
button click
with signals and without.
Using the code from the link that Luca posted, I decided to
print the
size of each column when a button press is received. As it
turns out, the
width of the columns is different in the two cases:
Without thread signals:
column[0](193) = 0 -> 193
cell[0] = min->109, natural->109
column[1](66) = 193 -> 259
cell[0] = min->20, natural->20
cell[1] = min->16, natural->16
cell[2] = min->35, natural->35
column[2](36) = 259 -> 295
cell[0] = min->16, natural->16
x = 105.872116, y = 259.547516
(x and y are the coordinates of the button press event)
With thread signals:
column[0](0) = 0 -> 0
cell[0] = min->135, natural->135
column[1](66) = 0 -> 66
cell[0] = min->20, natural->20
cell[1] = min->16, natural->16
cell[2] = min->35, natural->35
column[2](36) = 66 -> 102
cell[0] = min->16, natural->16
x = 113.528488, y = 158.563782
As you can see, the width of the first column is 0 when the
signals are
being emitted. As expected, if I were to click very close to
the left
border of the widget, the edit dialog does not get triggered as
the x
coordinate falls within column 1:
column[0](0) = 0 -> 0
cell[0] = min->135, natural->135
column[1](66) = 0 -> 66
cell[0] = min->20, natural->20
cell[1] = min->16, natural->16
cell[2] = min->35, natural->35
column[2](36) = 66 -> 102
cell[0] = min->16, natural->16
x = 21.247330, y = 181.310333
I could use the cell renderer width if the column width is 0
but that
seems unreliable since the cell renderer width is not the same
as the
column and it's also not static.
On Tue, Dec 18, 2018 at 8:23 AM Mitko Haralanov <
voidtrance gmail com
wrote:
I am not posting the complete function because there is a lot
of
irrelevant code. I am also not interested in the specific
cell renderer but
rather the row on which the click occurred.
tatic gboolean on_button_press_event(GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
GtkTreeView *treeview = GTK_TREE_VIEW(widget);
GdkEventButton *button = (GdkEventButton *)event;
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter;
GtkTreeViewColumn *column;
GtkScopeProjectEditDialog *dialog;
GtkScopeProjectEditData *pdata, fill;
GtkScopeProject *project;
guint response, index;
gboolean ret = FALSE;
if (button->type != GDK_BUTTON_PRESS ||
!gtk_tree_view_get_path_at_pos(treeview, button->x,
button->y,
&path, &column, NULL, NULL))
return FALSE;
index = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column),
"index"));
if (index != TREEVIEW_COLUMN_EDIT)
goto done;
model = gtk_tree_view_get_model(treeview);
if (!gtk_tree_model_get_iter(model, &iter, path))
goto done;
gtk_tree_model_get(model, &iter, PROJECT_COLUMN_OBJ,
&project, -1);
...
The issue is that the column which is returned by
gtk_tree_view_get_path_at_pos() is different depending on
whether thread
signals are being emitted vs not. I have verified that the
button press
coordinates are the same (button->x and button->y have the
same values in
both cases).
On Tue, Dec 18, 2018 at 5:24 AM Luca Bacci <
luca bacci982 gmail com
wrote:
Hi Mitko! Can you post here the code for the button-press
event
handler?
It should more or less follow the code here:
http://scentric.net/tutorial/sec-misc-get-renderer-from-click.html
Luca
Il giorno lun 17 dic 2018 alle ore 20:28 Mitko Haralanov
via gtk-list <
gtk-list gnome org
ha scritto:
Hi,
In my application, I want to be able to update a treeview
from a
separate thread. Each treeview row was a column that is a
progress bar. The
progress to be displayed is generated by a separate
thread as to not block
the UI.
Since GTK is not thread-safe, the way the application is
written is
that the thread, when it needs to emit a signal, will
prepare the signal
data and then call g_main_context_invoke_full(NULL, cb,
data, ...) in order
to be able to call g_singal_emit() in the global default
context thread.
The signal handler updates the tree model, which in turn
updates the tree
view.
For the most part this works with one big, ugly exception
- the same
treeview has a column, which is supposed to open the
item's Edit dialog
when clicked. So, naturally, I have a button-press
handler connected to the
treeview, which launches the Edit dialog when the button
press occurs in
the correct column.
However, when an update is running and the thread is
continuously
emitting signals, clicking on *any* column of *any* of
the other items
opens the Edit dialog. The treeview behaves as if the
items in it have only
one column.
Every example or document that I have seen in relation to
signals
from threads says to emit the signal from a g_idle_add()
handler. However,
g_main_context_invoke_full(NULL, ...) should be the same
as calling
g_idle_add().
Can someone shed some light into what might be happening?
Thank you.
_______________________________________________
gtk-list mailing list
gtk-list gnome org
https://mail.gnome.org/mailman/listinfo/gtk-list
_______________________________________________
gtk-list mailing list
gtk-list gnome org
https://mail.gnome.org/mailman/listinfo/gtk-list
_______________________________________________
gtk-list mailing list
gtk-list gnome org
https://mail.gnome.org/mailman/listinfo/gtk-list
_______________________________________________
gtk-list mailing list
gtk-list gnome org
https://mail.gnome.org/mailman/listinfo/gtk-list
_______________________________________________
gtk-list mailing list
gtk-list gnome org
https://mail.gnome.org/mailman/listinfo/gtk-list
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]