Re: GFileMonitor - g_signal_handler_block "changed" signal doesn't block handler?
On 03/30/2017 03:39 PM, David C. Rankin wrote: > Let me know what the experts think. There is something funny about the way > block/unblock works with GFileMonitor that I'm missing. For normal signals in > the app, I have no problems with block/unblock. I've posted this complete question to: http://stackoverflow.com/questions/43132025/gfilemonitor-g-signal-handler-block-changed-signal-doesnt-block-handler?noredirect=1#comment73342904_43132025 and I've uploaded the complete source to: https://github.com/drankinatty/gtktest if anyone needs additional detail about the issue. -- David C. Rankin, J.D.,P.E. ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
GFileMonitor - g_signal_handler_block "changed" signal doesn't block handler?
All I have implemented a GFileMonitor/g_file_monitor_file watch for respond to changes to the current file by an external process within an editor[1], but now issuing g_signal_handler_block to prevent the "changed" signal from being handled on normal saves, does not prevent the callback from firing. The basic flow is this, I set the watch with: GFile *gfile = g_file_new_for_path (app->filename); ... /* create monitor for app->filename */ app->filemon = g_file_monitor_file (gfile, G_FILE_MONITOR_NONE, cancellable, &err); ... /* connect changed signal to monitored file saving ID */ app->mfp_handler = g_signal_connect (G_OBJECT(app->filemon), "changed", G_CALLBACK (file_monitor_on_changed), data); Both the instance (app->filemon) and handler_id (app->mfp_handler) are saved. In order to prevent handling of changes during normal save/save as operations, I created block and unblock functions to prevent the changes to the file from firing the callback, e.g. show below with debug g_print calls: void file_monitor_block_changed (gpointer data) { kwinst *app = (kwinst *)data; if (!app->filemon || !app->mfp_handler) return; g_print ("blocking changed (%lu)\n", app->mfp_handler); g_signal_handler_block (app->filemon, app->mfp_handler); } void file_monitor_unblock_changed (gpointer data) { kwinst *app = (kwinst *)data; if (!app->filemon || !app->mfp_handler) return; g_print ("unblocking changed (%lu)\n", app->mfp_handler); g_signal_handler_unblock (app->filemon, app->mfp_handler); } To implement the block/unblock, I wrap the file 'save/save as' function in the block, then save, then unblock, but the callback is still firing on normal saves. e.g. in the save function I have: if (app->mfp_handler) /* current file monitor on file */ file_monitor_block_changed (app); /* block "changed" signal */ g_print (" buffer_write_file (app, filename)\n"); buffer_write_file (app, filename); /* write to file app->filename */ if (filename) file_monitor_add (app); /* setup monitoring on new name */ else if (app->mfp_handler) file_monitor_unblock_changed (app); /* unblock "changed" signal */ To my disbelief, the callback continues to fire as if I had not called block at all. For instance when saving the file, the debug output is: $ ./bin/gtkwrite blocking changed (669) buffer_write_file (app, filename) unblocking changed (669) Monitor Event: File = /home/david/tmp/foo.txt.UY9IXY G_FILE_MONITOR_EVENT_DELETED Monitor Event: File = /home/david/tmp/foo.txt G_FILE_MONITOR_EVENT_CREATED Monitor Event: File = /home/david/tmp/foo.txt G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT Monitor Event: File = /home/david/tmp/foo.txt G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED from which I can see the block called, the call to save the file to disk, the unblock call, and then the callback firing to delete the temp file 'foo.txt.UY9IXY', and create the new 'foo.txt' and then setting the attributes. Why is this occurring? I get the exact same firing of the callback if I completely remove the block/unblock calls, so it is not due to gio using a temp file with the g_file_set_contents call during save. When I created my custom signal for using inotify directly, I could block/unblock that signal without any problem, but when using GFileMonitor, the block/unblock seems like it is completely ignored. All types are correct, e.g. from the basic struct app: GFileMonitor*filemon; gulong mfp_handler; So what is the trick? How to I prevent the file_monitor_on_changed() callback from firing in response to the "changed" signal on normal file saves. I shouldn't have to completely disconnect/reconnect the callback should I? The block/unblock should be sufficient for temporary use, right? Let me know what the experts think. There is something funny about the way block/unblock works with GFileMonitor that I'm missing. For normal signals in the app, I have no problems with block/unblock. Footnote [1]: (see earlier post "Howto integrate an inotify watch on editor file in GTK+2 event loop?") -- David C. Rankin, J.D.,P.E. ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
On 30 March 2017 at 11:21, Gabriele Greco wrote: >> >> Thanks. While I can remember have read your explanations somewhere >> already, I really missed that g_main_context_invoke() function. > > > There is some difference/advantage on calling: > > g_main_context_invoke(NULL, func, data)? > > instead of > > g_idle_add(func, data); Calling g_main_context_invoke() will ensure that the main context is owned and acquired during the invocation. As I said, it will do the right thing depending on the type of context you are using. > As far as I can see from the function documentations g_main_context_invoke > seems useful when having multiple contexts on different threads, I'm wrong? Correct, but remember that different libraries may have different contexts in different threads already by the time you call your code. It's also a good rule of thumb to start using this function, as it will be more efficient once you start using API like GTask, with its own main context. Ciao, Emmanuele. -- https://www.bassi.io [@] ebassi [@gmail.com] ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
> > > Thanks. While I can remember have read your explanations somewhere > already, I really missed that g_main_context_invoke() function. There is some difference/advantage on calling: g_main_context_invoke(NULL, func, data)? instead of g_idle_add(func, data); As far as I can see from the function documentations g_main_context_invoke seems useful when having multiple contexts on different threads, I'm wrong? -- *Bye,* * Gabry* ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
On Thu, 2017-03-30 at 10:21 +0100, Emmanuele Bassi wrote: > g_idle_add() or, better yet, g_main_context_invoke(). Thanks. While I can remember have read your explanations somewhere already, I really missed that g_main_context_invoke() function. ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
On Thu, 2017-03-30 at 11:10 +0200, Nicola Fontana wrote: > > > As said you can leverage the main loop and unroll yours, e.g.: > > gboolean my_idle_callback() > { > gint n; > for (n = 0; n < 100; ++n) { > ... > } > return FALSE; > } > > should become: > > gboolean my_unrolled_idle_callback() > { > static gint n = 0; > ... > ++n; > return n < 100; > } > > Depending on your use case, the above could or could not be > possible. > Ah yes, that is an interesting idea. > > > > So I have to go back to my initial idea -- create a thread which > > gets a > > message for each "changed" signal, and then calls an idle function > > only > > for the last message in the queue each. I have to use > > gdk_threads_add_idle() then. > > This is probably the cleanest solution, although I don't > understand why you would have to use gdk_threads_add_idle(). I think gdk_threads_add_idle() is the recommended way to update the GTK GUI from inside a second thread. I am doing it in the current version of the editor already for querying and displaying extended symbol information, see https://github.com/ngtk3/NEd Here is where gdk_threads_add_idle() was suggested: https://developer.gnome.org/gdk3/stable/gdk3-Threads.html "You can schedule work in the main thread safely from other threads by using gdk_threads_add_idle() and gdk_threads_add_timeout()" > > > > > The task of that idle function in my current use case would be to > > get > > highlight syntax information from the IDE process called nimsuggest > > and > > then to apply the corresponding tags to gtksource textbuffer. > > IMO it is essential to split your code in two parts: (1) gathering > the highlight information and (2) applying it to the textbuffer. Yes, that was my thoughts too. It would imply some additional code, but may be the best solution. > > (1) can be run entirely in the working thread (hence non-blocking) > while (2) must be implemented as a callback to be run in the GTK+ > thread. I cannot believe (2) takes tenths of second. > > When you are ready from (1) you can spawn the idle callback with > g_source_attach()... no needs for gdk_threads_add_idle(). In the > following StackOverflow answer I provided an example in C: > > http://stackoverflow.com/questions/27950493/safety-of-using-pthreads- > in-gtk2-0-application#27990662 > Thanks for that suggestion, I will look at the stackoverflow example. And indeed, applying the text tags to the gtksource buffer is finally the only critical part. I dont know how fast that really is, I would assume that it takes less than 50 ms, what would be fine. If it really is slow, I may split it in multiple parts/calls. ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
On 30 March 2017 at 10:10, Nicola Fontana wrote: > When you are ready from (1) you can spawn the idle callback with > g_source_attach()... no needs for gdk_threads_add_idle(). In the > following StackOverflow answer I provided an example in C: A small correction: use g_main_context_invoke() instead, as it will always do the right thing. The only reason to prefer gdk_threads_add_idle() to g_idle_add() (or g_main_context_invoke()) is that your own code may depend on additional libraries that still attempt at acquiring the GDK thread lock — i.e. they use gdk_threads_enter()/gdk_threads_leave() inside a separate thread to mark a critical section. No newly written, maintained, or portable code should do that, so you can safely use g_idle_add() or, better yet, g_main_context_invoke(). Ciao, Emmanuele. -- https://www.bassi.io [@] ebassi [@gmail.com] ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
Il Thu, 30 Mar 2017 09:38:41 +0200 Stefan Salewski scrisse: > On Wed, 2017-03-29 at 23:26 +0200, Nicola Fontana wrote: > > > > > > idle functions do *not* run in the background so if you don't > > release the CPU you will experience what you described. > > > > AFAIK all the GMainLoop code is single-threaded hence, as a > > consequence, you will block the UI whenever the CPU is busy > > running your code, being it inside a signal handler, a timeout > > function or (as in your case) an idle function. > > > > Just avoid loops when you know they are expensive. Instead > > leverage the cyclic nature of GMainLoop for iterating over your > > code, i.e. by respawning your idle function as much as needed. > > > > In that case it is really a bad idea to have a outer loop in that idle > function :-( As said you can leverage the main loop and unroll yours, e.g.: gboolean my_idle_callback() { gint n; for (n = 0; n < 100; ++n) { ... } return FALSE; } should become: gboolean my_unrolled_idle_callback() { static gint n = 0; ... ++n; return n < 100; } Depending on your use case, the above could or could not be possible. > So I have to go back to my initial idea -- create a thread which gets a > message for each "changed" signal, and then calls an idle function only > for the last message in the queue each. I have to use > gdk_threads_add_idle() then. This is probably the cleanest solution, although I don't understand why you would have to use gdk_threads_add_idle(). > The task of that idle function in my current use case would be to get > highlight syntax information from the IDE process called nimsuggest and > then to apply the corresponding tags to gtksource textbuffer. IMO it is essential to split your code in two parts: (1) gathering the highlight information and (2) applying it to the textbuffer. (1) can be run entirely in the working thread (hence non-blocking) while (2) must be implemented as a callback to be run in the GTK+ thread. I cannot believe (2) takes tenths of second. When you are ready from (1) you can spawn the idle callback with g_source_attach()... no needs for gdk_threads_add_idle(). In the following StackOverflow answer I provided an example in C: http://stackoverflow.com/questions/27950493/safety-of-using-pthreads-in-gtk2-0-application#27990662 Ciao. -- Nicola ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
On Thu, 2017-03-30 at 09:38 +0200, Stefan Salewski wrote: > Currently I wonder if the call of gtk3.mainIteration() inside the > idle > function is possible and if it would help updating the display. In my > first draft of the Nim toy chess I used something like this > > while gtk3.eventsPending(): discard gtk3.mainIteration() Yes, that has an effect, it seems to allow GUI redraw, but keyboard input remains sluggish. If I add additional a sleep() function call keyboard seems to react better. But of course that is no real solution, so I have to test the thread idea next. ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
Re: Input event reduction
On Wed, 2017-03-29 at 23:26 +0200, Nicola Fontana wrote: > > > idle functions do *not* run in the background so if you don't > release the CPU you will experience what you described. > > AFAIK all the GMainLoop code is single-threaded hence, as a > consequence, you will block the UI whenever the CPU is busy > running your code, being it inside a signal handler, a timeout > function or (as in your case) an idle function. > > Just avoid loops when you know they are expensive. Instead > leverage the cyclic nature of GMainLoop for iterating over your > code, i.e. by respawning your idle function as much as needed. > In that case it is really a bad idea to have a outer loop in that idle function :-( So I have to go back to my initial idea -- create a thread which gets a message for each "changed" signal, and then calls an idle function only for the last message in the queue each. I have to use gdk_threads_add_idle() then. The task of that idle function in my current use case would be to get highlight syntax information from the IDE process called nimsuggest and then to apply the corresponding tags to gtksource textbuffer. Unfortunately that may take a few hundred milliseconds for each full update. Currently I wonder if the call of gtk3.mainIteration() inside the idle function is possible and if it would help updating the display. In my first draft of the Nim toy chess I used something like this while gtk3.eventsPending(): discard gtk3.mainIteration() to avoid display freeze. ___ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list