[glib: 4/6] gmain: Guarantee handler dispatch after receiving UNIX signal



commit 969b0e00b36d2ca4dddd2105d24574e56d2ea1ea
Author: Tomasz Miąsko <tomasz miasko gmail com>
Date:   Fri Nov 2 00:00:00 2018 +0000

    gmain: Guarantee handler dispatch after receiving UNIX signal
    
    Guarantee that user signal callback is dispatched _after_ receiving a
    signal as long as the handler expresses continued interest in receiving
    such a notification.
    
    Previously if a signal has been received during user callback dispatch
    but before pending flag had been cleared then the signal would be
    irrevocably lost.
    
    This is a very useful guarantee to have in cases where signals are used
    to signify a need for synchronization with external resources. For
    example: reloading configuration file after SIGUSR1 or retrieving a
    terminal size after SIGWINCH.

 glib/gmain.c      |  4 ++--
 glib/tests/unix.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 2 deletions(-)
---
diff --git a/glib/gmain.c b/glib/gmain.c
index 07ec867bc..85d0f2e43 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -5214,10 +5214,10 @@ g_unix_signal_watch_dispatch (GSource    *source,
       return FALSE;
     }
 
-  again = (callback) (user_data);
-
   g_atomic_int_set (&unix_signal_source->pending, FALSE);
 
+  again = (callback) (user_data);
+
   return again;
 }
 
diff --git a/glib/tests/unix.c b/glib/tests/unix.c
index 9d55a6c58..82ffd0f56 100644
--- a/glib/tests/unix.c
+++ b/glib/tests/unix.c
@@ -245,6 +245,56 @@ test_sighup_nested (void)
   g_main_loop_unref (mainloop);
 }
 
+static gboolean
+on_sigwinch_received (gpointer data)
+{
+  GMainLoop *loop = (GMainLoop *) data;
+
+  sig_counter ++;
+
+  if (sig_counter == 1)
+    kill (getpid (), SIGWINCH);
+  else if (sig_counter == 2)
+    g_main_loop_quit (loop);
+  else if (sig_counter > 2)
+    g_assert_not_reached ();
+
+  /* Increase the time window in which an issue could happen. */
+  g_usleep (G_USEC_PER_SEC);
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+test_callback_after_signal (void)
+{
+  /* Checks that user signal callback is invoked *after* receiving a signal.
+   * In other words a new signal is never merged with the one being currently
+   * dispatched or whose dispatch had already finished. */
+
+  GMainLoop *mainloop;
+  GMainContext *context;
+  GSource *source;
+
+  sig_counter = 0;
+
+  context = g_main_context_new ();
+  mainloop = g_main_loop_new (context, FALSE);
+
+  source = g_unix_signal_source_new (SIGWINCH);
+  g_source_set_callback (source, on_sigwinch_received, mainloop, NULL);
+  g_source_attach (source, context);
+  g_source_unref (source);
+
+  g_assert_cmpint (sig_counter, ==, 0);
+  kill (getpid (), SIGWINCH);
+  g_main_loop_run (mainloop);
+  g_assert_cmpint (sig_counter, ==, 2);
+
+  g_main_loop_unref (mainloop);
+  g_main_context_unref (context);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -259,6 +309,7 @@ main (int   argc,
   g_test_add_func ("/glib-unix/sighup_again", test_sighup);
   g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
   g_test_add_func ("/glib-unix/sighup_nested", test_sighup_nested);
+  g_test_add_func ("/glib-unix/callback_after_signal", test_callback_after_signal);
 
   return g_test_run();
 }


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