gst-launch: Use g_unix_signal_add() to handle keyboard interruption

Current implementation uses a traditional signal handler and a 250ms
timeout callback in the event loop.  Adding a GSource with
g_unix_signal_add() to the GMainLoop is a much more elegant solution.
The signal handler with this approach can send a message to the bus
directly rather than set a flag as all dispatching intricacies are handled
by GLib.

https://bugzilla.gnome.org/show_bug.cgi?id=693481
This commit is contained in:
Krzysztof Konopko 2013-02-13 00:27:28 +00:00 committed by Sebastian Dröge
parent 651ed1acd8
commit 6099b35f7e

View file

@ -25,36 +25,25 @@
# include "config.h" # include "config.h"
#endif #endif
/* FIXME: hack alert */ #include <glib.h>
#ifdef HAVE_WIN32
#define DISABLE_FAULT_HANDLER
#endif
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
#include <glib-unix.h>
#include <sys/wait.h> #include <sys/wait.h>
#endif #endif
#include <locale.h> /* for LC_ALL */ #include <locale.h> /* for LC_ALL */
#include "tools.h" #include "tools.h"
/* FIXME: This is just a temporary hack. We should have a better
* check for siginfo handling. */
#ifdef SA_SIGINFO
#define USE_SIGINFO
#endif
extern volatile gboolean glib_on_error_halt; extern volatile gboolean glib_on_error_halt;
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
static void fault_restore (void); static void fault_restore (void);
static void fault_spin (void); static void fault_spin (void);
static void sigint_restore (void);
static gboolean caught_intr = FALSE;
#endif #endif
/* event_loop return codes */ /* event_loop return codes */
@ -77,8 +66,7 @@ static gboolean waiting_eos = FALSE;
/* convenience macro so we don't have to litter the code with if(!quiet) */ /* convenience macro so we don't have to litter the code with if(!quiet) */
#define PRINT if(!quiet)g_print #define PRINT if(!quiet)g_print
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
#ifndef USE_SIGINFO
static void static void
fault_handler_sighandler (int signum) fault_handler_sighandler (int signum)
{ {
@ -102,34 +90,6 @@ fault_handler_sighandler (int signum)
fault_spin (); fault_spin ();
} }
#else /* USE_SIGINFO */
static void
fault_handler_sigaction (int signum, siginfo_t * si, void *misc)
{
fault_restore ();
/* printf is used instead of g_print(), since it's less likely to
* deadlock */
switch (si->si_signo) {
case SIGSEGV:
fprintf (stderr, "Caught SIGSEGV accessing address %p\n", si->si_addr);
break;
case SIGQUIT:
if (!quiet)
printf ("Caught SIGQUIT\n");
break;
default:
fprintf (stderr, "signo: %d\n", si->si_signo);
fprintf (stderr, "errno: %d\n", si->si_errno);
fprintf (stderr, "code: %d\n", si->si_code);
break;
}
fault_spin ();
}
#endif /* USE_SIGINFO */
static void static void
fault_spin (void) fault_spin (void)
{ {
@ -167,17 +127,12 @@ fault_setup (void)
struct sigaction action; struct sigaction action;
memset (&action, 0, sizeof (action)); memset (&action, 0, sizeof (action));
#ifdef USE_SIGINFO
action.sa_sigaction = fault_handler_sigaction;
action.sa_flags = SA_SIGINFO;
#else
action.sa_handler = fault_handler_sighandler; action.sa_handler = fault_handler_sighandler;
#endif
sigaction (SIGSEGV, &action, NULL); sigaction (SIGSEGV, &action, NULL);
sigaction (SIGQUIT, &action, NULL); sigaction (SIGQUIT, &action, NULL);
} }
#endif /* DISABLE_FAULT_HANDLER */ #endif /* G_OS_UNIX */
#if 0 #if 0
typedef struct _GstIndexStats typedef struct _GstIndexStats
@ -503,70 +458,27 @@ print_toc_entry (gpointer data, gpointer user_data)
g_list_foreach (subentries, print_toc_entry, GUINT_TO_POINTER (indent)); g_list_foreach (subentries, print_toc_entry, GUINT_TO_POINTER (indent));
} }
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
/* we only use sighandler here because the registers are not important */ /* As the interrupt handler is dispatched from GMainContext as a GSourceFunc
static void * handler, we can react to this by posting a message. */
sigint_handler_sighandler (int signum)
{
PRINT ("Caught interrupt -- ");
/* If we were waiting for an EOS, we still want to catch
* the next signal to shutdown properly (and the following one
* will quit the program). */
if (waiting_eos) {
waiting_eos = FALSE;
} else {
sigint_restore ();
}
/* we set a flag that is checked by the mainloop, we cannot do much in the
* interrupt handler (no mutex or other blocking stuff) */
caught_intr = TRUE;
}
/* is called every 250 milliseconds (4 times a second), the interrupt handler
* will set a flag for us. We react to this by posting a message. */
static gboolean static gboolean
check_intr (GstElement * pipeline) intr_handler (gpointer user_data)
{ {
if (!caught_intr) { GstElement *pipeline = (GstElement *) user_data;
return TRUE;
} else {
caught_intr = FALSE;
PRINT ("handling interrupt.\n");
/* post an application specific message */ PRINT ("handling interrupt.\n");
gst_element_post_message (GST_ELEMENT (pipeline),
gst_message_new_application (GST_OBJECT (pipeline),
gst_structure_new ("GstLaunchInterrupt",
"message", G_TYPE_STRING, "Pipeline interrupted", NULL)));
/* remove timeout handler */ /* post an application specific message */
return FALSE; gst_element_post_message (GST_ELEMENT (pipeline),
} gst_message_new_application (GST_OBJECT (pipeline),
gst_structure_new ("GstLaunchInterrupt",
"message", G_TYPE_STRING, "Pipeline interrupted", NULL)));
/* remove signal handler */
return FALSE;
} }
static void #endif /* G_OS_UNIX */
sigint_setup (void)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_handler = sigint_handler_sighandler;
sigaction (SIGINT, &action, NULL);
}
static void
sigint_restore (void)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_handler = SIG_DFL;
sigaction (SIGINT, &action, NULL);
}
#endif /* DISABLE_FAULT_HANDLER */
/* returns ELR_ERROR if there was an error /* returns ELR_ERROR if there was an error
* or ELR_INTERRUPT if we caught a keyboard interrupt * or ELR_INTERRUPT if we caught a keyboard interrupt
@ -574,8 +486,8 @@ sigint_restore (void)
static EventLoopResult static EventLoopResult
event_loop (GstElement * pipeline, gboolean blocking, GstState target_state) event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
{ {
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
gulong timeout_id; guint signal_watch_id;
#endif #endif
GstBus *bus; GstBus *bus;
GstMessage *message = NULL; GstMessage *message = NULL;
@ -584,8 +496,9 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
bus = gst_element_get_bus (GST_ELEMENT (pipeline)); bus = gst_element_get_bus (GST_ELEMENT (pipeline));
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
timeout_id = g_timeout_add (250, (GSourceFunc) check_intr, pipeline); signal_watch_id =
g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, pipeline);
#endif #endif
while (TRUE) { while (TRUE) {
@ -856,8 +769,8 @@ exit:
if (message) if (message)
gst_message_unref (message); gst_message_unref (message);
gst_object_unref (bus); gst_object_unref (bus);
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
g_source_remove (timeout_id); g_source_remove (signal_watch_id);
#endif #endif
return res; return res;
} }
@ -981,11 +894,9 @@ main (int argc, char *argv[])
gst_tools_print_version (); gst_tools_print_version ();
#ifndef DISABLE_FAULT_HANDLER #ifdef G_OS_UNIX
if (!no_fault) if (!no_fault)
fault_setup (); fault_setup ();
sigint_setup ();
#endif #endif
/* make a null-terminated version of argv */ /* make a null-terminated version of argv */