diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-05-09 19:08:32 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-05-09 19:08:32 +0200 |
commit | f557af9a9112955d3b65f6ad0d54c0791189f961 (patch) | |
tree | 9d7718cb1571b624710a182f09aca7163dc673a0 /src/interface-gtk | |
parent | bac1efa51bf889d494013925586e87ef08307529 (diff) | |
download | sciteco-f557af9a9112955d3b65f6ad0d54c0791189f961.tar.gz |
fixed CTRL+C interruptions on Windows; optimized CTRL+C polling on Gtk+
* teco_interrupt() turned out to be unsuitable to kill child processes (eg. when <EB> hangs).
Instead, we have Win32-specific code now.
* Since SIGINT can be ignored on UNIX, pressing CTRL+C was not guaranteed to kill the
child process (eg. when <EB> hangs).
At the same time, it makes sense to send SIGINT first, so programs can terminate gracefully.
The behaviour has therefore been adapted: Interrupting with CTRL+C the first time will kill
gracefully. The second time, a more agressive signal is sent to kill the child process.
Unfortunately, this would be relatively tricky and complicated to do on Windows, so CTRL+C will always
"hard-kill" the child process.
* Moreover, teco_interrupt() killed the entire process on Windows when called the second time.
This resulted in any interruption to terminate SciTECO unexpectedly when tried the second time on Gtk/Win32.
* teco_sigint_occurred renamed to teco_interrupted:
There may be several different sources for setting this flag.
* Checking for CTRL+C on Gtk involves driving the main event loop repeatedly.
This is a very expensive operation. We now do that only every 100ms. This is still sufficient since
keyboard input comes from humans.
This optimization saves 75% runtime on Windows and 90% on Linux.
* The same optimization turned out to be contraproductive on PDCurses/WinGUI.
Diffstat (limited to 'src/interface-gtk')
-rw-r--r-- | src/interface-gtk/interface.c | 47 |
1 files changed, 25 insertions, 22 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index d22009b..6301a71 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -70,6 +70,12 @@ static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny * gpointer user_data); static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED; +/** + * Interval between polling for keypresses. + * In other words, this is the maximum latency to detect CTRL+C interruptions. + */ +#define TECO_POLL_INTERVAL 100000 /* microseconds */ + #define UNNAMED_FILE "(Unnamed)" #define USER_CSS_FILE ".teco_css" @@ -719,22 +725,27 @@ teco_interface_popup_clear(void) * system call overhead. * But the GDK lock that would be necessary for synchronization * has been deprecated. - * - * @todo It would be great to have platform-specific optimizations, - * so we can detect interruptions without having to drive the Glib - * event loop (e.g. using libX11 or Win32 APIs). - * There already is a keyboard hook for Win32 in interface-curses. - * On the downside, such solutions will probably freeze the window - * while SciTECO is busy. However we currently freeze the window - * anyway while being busy to avoid flickering. */ gboolean teco_interface_is_interrupted(void) { - if (gtk_main_level() > 0) - gtk_main_iteration_do(FALSE); + if (!gtk_main_level()) + /* batch mode */ + return teco_interrupted != FALSE; - return teco_sigint_occurred != FALSE; + /* + * By polling only every TECO_POLL_INTERVAL microseconds + * we save 75-90% of runtime. + */ + static guint64 last_poll_ts = 0; + guint64 now_ts = g_get_monotonic_time(); + + if (G_LIKELY(last_poll_ts+TECO_POLL_INTERVAL > now_ts)) + return teco_interrupted != FALSE; + last_poll_ts = now_ts; + + gtk_main_iteration_do(FALSE); + return teco_interrupted != FALSE; } static void @@ -1172,12 +1183,10 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us gdk_keyval_to_upper(event->keyval) == GDK_KEY_C) /* * Handle asynchronous interruptions if CTRL+C is pressed. - * This will usually send SIGINT to the entire process - * group and set `teco_sigint_occurred`. * If the execution thread is currently blocking, * the key is delivered like an ordinary key press. */ - teco_interrupt(); + teco_interrupted = TRUE; else g_queue_push_tail(teco_interface.event_queue, gdk_event_copy((GdkEvent *)event)); @@ -1209,9 +1218,9 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us */ gdk_window_freeze_updates(top_window); - teco_sigint_occurred = FALSE; + teco_interrupted = FALSE; teco_interface_handle_key_press(&event->key, &error); - teco_sigint_occurred = FALSE; + teco_interrupted = FALSE; gdk_window_thaw_updates(top_window); @@ -1256,12 +1265,6 @@ static gboolean teco_interface_sigterm_handler(gpointer user_data) { /* - * Since this handler replaces the default signal handler, - * we also have to make sure it interrupts. - */ - teco_interrupt(); - - /* * Similar to window deletion - emulate "close" key press. */ g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS); |